# # ╔╦╗╔═╗╔═╗╔═╗╔═╗ # ║║║╠═╣║ ║ ║╚═╗ # ╩ ╩╩ ╩╚═╝╚═╝╚═╝ - name: Collect fleetd logs platform: macos description: Copies logs from the fleetd agent to a shared folder. script: | cp /var/log/orbit/orbit.stderr.log ~/Library/Logs/Fleet/fleet-desktop.log /Users/Shared echo "Successfully copied fleetd logs to the /Users/Shared folder." echo "To retrieve logs, ask the end user to open Finder and in the menu bar select Go > Go to Folder." echo "Then, ask the end user to type in /Users/Shared, press Return, and locate orbit.stderr.log (Orbit logs) and fleet-desktop.log (Fleet Desktop logs) files." - name: Create conditional access allow file platform: macos description: Create an allow file used for Microsoft Entra conditional access. script: | #!/bin/bash # Script to check for entra-conditional-access-allow file and create it if needed # Target location: /var/fleet/entra-conditional-access-allow FILE_PATH="/var/fleet/entra-conditional-access-allow" DIR_PATH="/var/fleet" # Check if the directory exists, create if it doesn't if [ ! -d "$DIR_PATH" ]; then echo "Directory $DIR_PATH does not exist. Creating it..." sudo mkdir -p "$DIR_PATH" fi # Check if the file exists if [ -f "$FILE_PATH" ]; then echo "File $FILE_PATH already exists." # Check current permissions CURRENT_PERMS=$(stat -f "%Lp" "$FILE_PATH" 2>/dev/null) if [ "$CURRENT_PERMS" != "644" ]; then echo "Current permissions: $CURRENT_PERMS. Updating to 644..." sudo chmod 644 "$FILE_PATH" echo "Permissions updated to 644." else echo "Permissions are already set to 644." fi else echo "File $FILE_PATH does not exist. Creating it..." # Create the file using touch sudo touch "$FILE_PATH" # Set permissions to 644 sudo chmod 644 "$FILE_PATH" echo "File created with permissions 644." fi # Verify the file and permissions if [ -f "$FILE_PATH" ]; then FINAL_PERMS=$(stat -f "%Lp" "$FILE_PATH" 2>/dev/null) echo "✓ File exists at: $FILE_PATH" echo "✓ Permissions: $FINAL_PERMS" else echo "✗ Error: Failed to create file" exit 1 fi - name: Refetch host platform: macos description: A script to refetch host details using Fleet's device authentication token. This script reads the device token from /opt/orbit/identifier and triggers a refetch. script: | #!/bin/bash set -euo pipefail # Exit on error, undefined vars, and pipe failures # Configuration IDENTIFIER_FILE="/opt/orbit/identifier" FLEET_URL="https://dogfood.fleetdm.com" # Set this environment variable or modify the script LOG_LEVEL="${LOG_LEVEL:-info}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Logging function log() { local level="$1" shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') case "$level" in "error") echo -e "${timestamp} [${RED}ERROR${NC}] $message" >&2 ;; "warn") echo -e "${timestamp} [${YELLOW}WARN${NC}] $message" >&2 ;; "info") echo -e "${timestamp} [${GREEN}INFO${NC}] $message" ;; "debug") if [[ "$LOG_LEVEL" == "debug" ]]; then echo -e "${timestamp} [DEBUG] $message" fi ;; esac } # Function to check if a command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Function to validate URL format validate_url() { local url="$1" if [[ ! "$url" =~ ^https?://[^[:space:]]+$ ]]; then return 1 fi return 0 } # Function to read device token read_device_token() { local token_file="$1" if [[ ! -f "$token_file" ]]; then log error "Device token file not found: $token_file" return 1 fi if [[ ! -r "$token_file" ]]; then log error "Cannot read device token file: $token_file (permission denied)" return 1 fi local token token=$(cat "$token_file" 2>/dev/null) if [[ -z "$token" ]]; then log error "Device token file is empty: $token_file" return 1 fi # Basic validation - Fleet device tokens should be non-empty strings if [[ ${#token} -lt 10 ]]; then log warn "Device token seems unusually short (${#token} characters)" fi echo "$token" } # Function to get host ID from device token get_host_id() { local fleet_url="$1" local device_token="$2" log debug "Attempting to get host ID using device token..." # Use the device endpoint to get basic host information local response local http_code response=$(curl -s -w "HTTPSTATUS:%{http_code}" \ -H "Accept: application/json" \ -H "User-Agent: fleet-refetch-script/1.0" \ --max-time 30 \ --retry 2 \ --retry-delay 1 \ "${fleet_url}/api/v1/fleet/device/${device_token}" 2>/dev/null) if [[ $? -ne 0 ]]; then log error "Failed to connect to Fleet server at $fleet_url" return 1 fi http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g') log debug "HTTP response code: $http_code" if [[ "$http_code" -ne 200 ]]; then log error "Failed to authenticate with device token (HTTP $http_code)" if [[ "$http_code" -eq 401 ]]; then log error "Device token appears to be invalid or expired" elif [[ "$http_code" -eq 404 ]]; then log error "Device not found or Fleet server endpoint not available" fi return 1 fi # Extract host ID from JSON response local host_id if command_exists jq; then host_id=$(echo "$response_body" | jq -r '.host.id' 2>/dev/null) else # Fallback: basic grep/sed extraction (less reliable but doesn't require jq) host_id=$(echo "$response_body" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2) fi if [[ -z "$host_id" || "$host_id" == "null" ]]; then log error "Could not extract host ID from response" log debug "Response body: $response_body" return 1 fi echo "$host_id" } # Function to trigger device-level refetch using device token trigger_device_refetch() { local fleet_url="$1" local device_token="$2" log info "Triggering device refetch using device token..." # Try device-specific refetch endpoint that may accept device tokens local response local http_code # First attempt: Try device-specific refetch endpoint response=$(curl -s -w "HTTPSTATUS:%{http_code}" \ -X POST \ -H "Accept: application/json" \ -H "User-Agent: fleet-refetch-script/1.0" \ --max-time 30 \ --retry 2 \ --retry-delay 1 \ "${fleet_url}/api/v1/fleet/device/${device_token}/refetch" 2>/dev/null) if [[ $? -ne 0 ]]; then log warn "Failed to connect to device-specific refetch endpoint, trying alternative..." return 1 fi http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g') log debug "Device refetch HTTP response code: $http_code" case "$http_code" in 200|202) log info "Device refetch triggered successfully" return 0 ;; 404) log warn "Device-specific refetch endpoint not available, trying alternative method..." return 1 ;; 401|403) log error "Device token authentication failed for refetch" return 1 ;; *) log warn "Device refetch returned HTTP $http_code, trying alternative method..." return 1 ;; esac } # Function to trigger refetch via orbit/fleetd ping mechanism trigger_orbit_ping() { local fleet_url="$1" local device_token="$2" log info "Attempting to trigger refetch via orbit ping mechanism..." local response local http_code # Try the orbit device ping endpoint which may trigger a refetch response=$(curl -s -w "HTTPSTATUS:%{http_code}" \ -X POST \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "User-Agent: fleet-refetch-script/1.0" \ -d '{"node_key": "'$device_token'", "refetch_requested": true}' \ --max-time 30 \ --retry 2 \ --retry-delay 1 \ "${fleet_url}/api/fleet/orbit/ping" 2>/dev/null) if [[ $? -ne 0 ]]; then log warn "Failed to connect to orbit ping endpoint" return 1 fi http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g') log debug "Orbit ping HTTP response code: $http_code" case "$http_code" in 200) log info "Orbit ping successful - refetch may have been triggered" return 0 ;; 404) log warn "Orbit ping endpoint not available" return 1 ;; 401|403) log warn "Authentication failed for orbit ping" return 1 ;; *) log warn "Orbit ping returned HTTP $http_code" return 1 ;; esac } # Function to simulate osquery check-in to trigger refetch trigger_osquery_checkin() { local fleet_url="$1" local device_token="$2" log info "Attempting to trigger refetch via osquery distributed read..." local response local http_code # Simulate an osquery distributed read which should trigger refetch if requested response=$(curl -s -w "HTTPSTATUS:%{http_code}" \ -X POST \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "User-Agent: fleet-refetch-script/1.0" \ -d '{"node_key": "'$device_token'"}' \ --max-time 30 \ --retry 2 \ --retry-delay 1 \ "${fleet_url}/api/v1/osquery/distributed/read" 2>/dev/null) if [[ $? -ne 0 ]]; then log warn "Failed to connect to osquery distributed endpoint" return 1 fi http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g') log debug "Osquery distributed read HTTP response code: $http_code" case "$http_code" in 200) # Check if refetch queries were returned if echo "$response_body" | grep -q "SELECT\|osquery_info\|system_info"; then log info "Osquery check-in successful - refetch queries may have been delivered" return 0 else log info "Osquery check-in successful but no refetch queries detected" return 1 fi ;; 404) log warn "Osquery distributed endpoint not available" return 1 ;; 401|403) log warn "Authentication failed for osquery distributed read" return 1 ;; *) log warn "Osquery distributed read returned HTTP $http_code" return 1 ;; esac } # Main refetch function that tries multiple approaches trigger_refetch() { local fleet_url="$1" local device_token="$2" local host_id="$3" log info "Attempting to trigger host refetch using device token..." # Try multiple approaches in order of preference # Method 1: Device-specific refetch endpoint if trigger_device_refetch "$fleet_url" "$device_token"; then return 0 fi # Method 2: Orbit ping mechanism if trigger_orbit_ping "$fleet_url" "$device_token"; then return 0 fi # Method 3: Osquery distributed read (may trigger refetch) if trigger_osquery_checkin "$fleet_url" "$device_token"; then return 0 fi # If all methods failed, provide guidance log error "All refetch methods failed with the device token" log error "" log error "POSSIBLE SOLUTIONS:" log error "1. The device might not have Fleet Desktop installed" log error "2. Try restarting the fleetd/orbit service on this host to trigger a natural check-in" log error "3. Use an API token with admin/maintainer privileges instead:" log error " curl -X POST -H 'Authorization: Bearer YOUR_API_TOKEN' \\" log error " '${fleet_url}/api/v1/fleet/hosts/${host_id}/refetch'" return 1 } # Function to display usage usage() { cat << EOF Usage: $0 [OPTIONS] Refetch host details using Fleet's device authentication token. OPTIONS: -u, --url URL Fleet server URL (can also be set via FLEET_URL env var) -f, --file FILE Path to device token file (default: /opt/orbit/identifier) -v, --verbose Enable debug logging -h, --help Show this help message EXAMPLES: # Basic usage with Fleet URL as environment variable export FLEET_URL="https://fleet.example.com" $0 # Specify Fleet URL directly $0 --url "https://fleet.example.com" # Use custom token file location $0 --url "https://fleet.example.com" --file "/custom/path/to/token" # Enable verbose logging $0 --url "https://fleet.example.com" --verbose NOTES: - The device must have Fleet Desktop installed - The device token is read from $IDENTIFIER_FILE by default - The Fleet server URL must include the protocol (http:// or https://) - This script requires curl to be installed - Optional: jq for better JSON parsing (will fall back to basic parsing if not available) - The refetch operation may require elevated privileges depending on Fleet configuration EOF } # Main function main() { local fleet_url="$FLEET_URL" local token_file="$IDENTIFIER_FILE" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -u|--url) fleet_url="$2" shift 2 ;; -f|--file) token_file="$2" shift 2 ;; -v|--verbose) LOG_LEVEL="debug" shift ;; -h|--help) usage exit 0 ;; *) log error "Unknown option: $1" usage exit 1 ;; esac done # Validation if [[ -z "$fleet_url" ]]; then log error "Fleet server URL is required" log error "Set FLEET_URL environment variable or use --url option" usage exit 1 fi if ! validate_url "$fleet_url"; then log error "Invalid Fleet server URL: $fleet_url" log error "URL must start with http:// or https://" exit 1 fi # Remove trailing slash from URL fleet_url="${fleet_url%/}" # Check dependencies if ! command_exists curl; then log error "curl is required but not installed" exit 1 fi if ! command_exists jq; then log warn "jq not found - will use basic JSON parsing (less reliable)" fi log info "Starting Fleet host refetch process..." log debug "Fleet URL: $fleet_url" log debug "Token file: $token_file" # Read device token log info "Reading device authentication token..." local device_token if ! device_token=$(read_device_token "$token_file"); then exit 1 fi log debug "Device token length: ${#device_token} characters" # Get host ID log info "Authenticating with Fleet server..." local host_id if ! host_id=$(get_host_id "$fleet_url" "$device_token"); then exit 1 fi log info "Successfully authenticated - Host ID: $host_id" # Trigger refetch if trigger_refetch "$fleet_url" "$device_token" "$host_id"; then log info "Host refetch completed successfully" log info "Note: It may take a few moments for the updated data to be available" exit 0 else log error "Host refetch failed" exit 1 fi } # Run main function with all arguments main "$@" - name: Uninstall fleetd platform: macos description: Removes the fleetd agent from a macOS device. script: | #!/bin/sh # Please don't delete. This script is referenced in the guide here: https://fleetdm.com/guides/how-to-uninstall-fleetd if [ $(id -u) -ne 0 ]; then echo "Please run as root" exit 1 fi function remove_fleet { set -x rm -rf /Library/LaunchDaemons/com.fleetdm.orbit.plist /var/lib/orbit /usr/local/bin/orbit /var/log/orbit /opt/orbit/ pkgutil --forget com.fleetdm.orbit.base.pkg || true launchctl stop com.fleetdm.orbit launchctl unload /Library/LaunchDaemons/com.fleetdm.orbit.plist pkill fleet-desktop || true # Check MDM status on a macOS device mdm_status=$(profiles status -type enrollment) # Check for MDM enrollment status and cleanup enrollment profile if echo "$mdm_status" | grep -q "MDM enrollment: Yes"; then echo "This Mac is MDM enrolled. Removing enrollment profile." profiles remove -identifier com.fleetdm.fleet.mdm.apple elif echo "$mdm_status" | grep -q "MDM enrollment: No"; then echo "This Mac is not MDM enrolled." else echo "MDM status is unknown." fi } if [ "$1" = "remove" ]; then # We are in the detached child process # Give the parent process time to report the success before removing echo "inside remove process" >>/tmp/fleet_remove_log.txt sleep 15 # We are root remove_fleet >>/tmp/fleet_remove_log.txt 2>&1 else # We are in the parent shell, start the detached child and return success echo "Removing fleetd, system will be unenrolled in 15 seconds..." echo "Executing detached child process" # We are root bash -c "bash $0 remove >/dev/null 2>/dev/null /&&!//&&!/}/{gsub("^[[:space:]]*","",$0);print "\x22"$1"\x22"":""\x22"$2"\x22"}' | /usr/bin/sed 's/^/{/;s/,$/}/' | /usr/bin/jq -r '.SSID')" /usr/sbin/ipconfig setverbose 0 /usr/bin/defaults write /Library/Preferences/com.apple.wifi.known-networks wifi.network.ssid."$ntwknm" -dict-add AutoJoinDisabled -bool TRUE # # ╦ ╦╦╔╗╔╔╦╗╔═╗╦ ╦╔═╗ # ║║║║║║║ ║║║ ║║║║╚═╗ # ╚╩╝╩╝╚╝═╩╝╚═╝╚╩╝╚═╝ - name: Create admin user platform: windows description: Creates an Admin user on Windows device. script: | $Username = "IT admin" $Password = ConvertTo-SecureString "StrongPassword123!" -AsPlainText -Force # Create the local user account New-LocalUser -Name $Username -Password $Password -FullName "Fleet IT admin" -Description "Admin account used to login when the end user forgets their password or the host is returned to Fleet." -AccountNeverExpires # Add the user to the Administrators group Add-LocalGroupMember -Group "Administrators" -Member $Username - name: Enable Windows defender platform: windows description: Enable Windows Defender anti-virus. script: | # Based on commands found here: https://support.huntress.io/hc/en-us/articles/4402989131283-Enable-Microsoft-Defender-via-PowerShell # Enable Real-Time Monitoring Set-MpPreference -DisableRealtimeMonitoring $false # Enable IOAV Protection Set-MpPreference -DisableIOAVProtection $false # Create Registry Key for Real-Time Protection New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name "Real-Time Protection" -Force # Enable Behavior Monitoring New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" -Name "DisableBehaviorMonitoring" -Value 0 -PropertyType DWORD -Force # Enable On-Access Protection New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" -Name "DisableOnAccessProtection" -Value 0 -PropertyType DWORD -Force # Ensure Scans Run When Real-Time Protection is Enabled New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" -Name "DisableScanOnRealtimeEnable" -Value 0 -PropertyType DWORD -Force # Ensure AntiSpyware is Enabled New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -Value 0 -PropertyType DWORD -Force # Start Windows Defender Services Start-Service -Name WinDefend Start-Service -Name WdNisSvc - name: Turn off Windows MDM platform: windows description: Unenrolls a Windows device from an MDM solution. script: | Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class MdmRegistration { [DllImport("mdmregistration.dll", SetLastError = true)] public static extern int UnregisterDeviceWithManagement(IntPtr pDeviceID); public static int UnregisterDevice() { return UnregisterDeviceWithManagement(IntPtr.Zero); } } "@ -Language CSharp try { $result = [MdmRegistration]::UnregisterDevice() if ($result -ne 0) { throw "UnregisterDeviceWithManagement failed with error code: $result" } Write-Host "Device unregistration called successfully." } catch { Write-Error "Error calling UnregisterDeviceWithManagement: $_" } - name: Uninstall fleetd platform: windows description: Removes the fleetd agent from a Windows device. script: | function Test-Administrator { [OutputType([bool])] param() process { [Security.Principal.WindowsPrincipal]$user = [Security.Principal.WindowsIdentity]::GetCurrent(); return $user.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator); } } # borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx function Resolve-Error-Detailed($ErrorRecord = $Error[0]) { $error_message = "========== ErrorRecord:{0}ErrorRecord.InvocationInfo:{1}Exception:{2}" $formatted_errorRecord = $ErrorRecord | format-list * -force | out-string $formatted_invocationInfo = $ErrorRecord.InvocationInfo | format-list * -force | out-string $formatted_exception = "" $Exception = $ErrorRecord.Exception for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) { $formatted_exception += ("$i" * 70) + "-----" $formatted_exception += $Exception | format-list * -force | out-string $formatted_exception += "-----" } return $error_message -f $formatted_errorRecord, $formatted_invocationInfo, $formatted_exception } #Stops Orbit service and related processes function Stop-Orbit { # Stop Service Stop-Service -Name "Fleet osquery" -ErrorAction "Continue" Start-Sleep -Milliseconds 1000 # Ensure that no process left running Get-Process -Name "orbit" -ErrorAction "SilentlyContinue" | Stop-Process -Force Get-Process -Name "osqueryd" -ErrorAction "SilentlyContinue" | Stop-Process -Force Get-Process -Name "fleet-desktop" -ErrorAction "SilentlyContinue" | Stop-Process -Force Start-Sleep -Milliseconds 1000 } #Remove Orbit footprint from registry and disk function Force-Remove-Orbit { try { #Stoping Orbit Stop-Orbit #Remove Service $service = Get-WmiObject -Class Win32_Service -Filter "Name='Fleet osquery'" if ($service) { $service.delete() | Out-Null } #Removing Program files entries $targetPath = $Env:Programfiles + "\\Orbit" Remove-Item -LiteralPath $targetPath -Force -Recurse -ErrorAction "Continue" #Remove HKLM registry entries Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse -ErrorAction "SilentlyContinue" | Where-Object {($_.ValueCount -gt 0)} | ForEach-Object { # Filter for osquery entries $properties = Get-ItemProperty $_.PSPath -ErrorAction "SilentlyContinue" | Where-Object {($_.DisplayName -eq "Fleet osquery")} if ($properties) { #Remove Registry Entries $regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $_.PSChildName Get-Item $regKey -ErrorAction "SilentlyContinue" | Remove-Item -Force -ErrorAction "SilentlyContinue" return } } # Write success log "Fleetd successfully removed at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt" } catch { Write-Host "There was a problem running Force-Remove-Orbit" Write-Host "$(Resolve-Error-Detailed)" # Write error log "Error removing fleetd at $(Get-Date): $($Error[0])" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt" return $false } return $true } function Main { try { # Is Administrator check if (-not (Test-Administrator)) { Write-Host "Please run this script with admin privileges." Exit -1 } Write-Host "About to uninstall fleetd..." if ($args[0] -eq "remove") { # "remove" is received as argument to the script when called as the # sub-process that will actually remove the fleet agent. # Log the start of removal process "Starting removal process at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt" # sleep to give time to fleetd to send the script results to Fleet Start-Sleep -Seconds 20 if (Force-Remove-Orbit) { Write-Host "fleetd was uninstalled." Exit 0 } else { Write-Host "There was a problem uninstalling fleetd." Exit -1 } } else { # when this script is executed from fleetd, it does not immediately # remove the agent. Instead, it starts a new detached process that # will do the actual removal. Write-Host "Removing fleetd, system will be unenrolled in 20 seconds..." Write-Host "Executing detached child process" $execName = $MyInvocation.ScriptName $proc = Start-Process -PassThru -FilePath "powershell" -WindowStyle Hidden -ArgumentList "-MTA", "-ExecutionPolicy", "Bypass", "-File", "$execName remove" # Log the process ID "Started removal process with ID: $($proc.Id) at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt" Start-Sleep -Seconds 5 # give time to process to start running Write-Host "Removal process started: $($proc.Id)." } } catch { Write-Host "Error: Entry point" Write-Host "$(Resolve-Error-Detailed)" Exit -1 } } # Execute the script with arguments passed to it Main $args[0] - name: Uninstall Slack platform: windows description: Removes Slack from a Windows device. script: | # Slack Uninstall Script # This script handles both MSI and EXE installations, including per-user installations $softwareName = "Slack" $exitCode = 0 $uninstalled = $false Write-Host "Starting Slack uninstallation process..." # Function to uninstall MSI packages function Remove-SlackMSI { Write-Host "Checking for MSI-based Slack installations..." # Find all Slack MSI products $msiProducts = Get-WmiObject -Class Win32_Product -Filter "Name LIKE '%Slack%'" -ErrorAction SilentlyContinue if ($msiProducts) { foreach ($product in $msiProducts) { Write-Host "Found MSI: $($product.Name) - Version: $($product.Version)" Write-Host "Attempting to uninstall MSI..." try { $result = $product.Uninstall() if ($result.ReturnValue -eq 0) { Write-Host "Successfully uninstalled MSI: $($product.Name)" return $true } else { Write-Host "MSI uninstall returned code: $($result.ReturnValue)" } } catch { Write-Host "Error uninstalling MSI: $_" } } } # Also try using msiexec with product codes from registry $msiKeys = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' ) foreach ($keyPath in $msiKeys) { $keys = Get-ChildItem -Path $keyPath -ErrorAction SilentlyContinue | ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } foreach ($key in $keys) { if ($key.DisplayName -like "*Slack*" -and $key.PSChildName -match '^{[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}}$') { Write-Host "Found MSI product code: $($key.PSChildName)" Write-Host "Attempting msiexec uninstall..." $msiArgs = @("/x", $key.PSChildName, "/qn", "/norestart", "REBOOT=ReallySuppress") $process = Start-Process -FilePath "msiexec.exe" -ArgumentList $msiArgs -Wait -PassThru -NoNewWindow if ($process.ExitCode -eq 0) { Write-Host "Successfully uninstalled via msiexec" return $true } else { Write-Host "msiexec returned exit code: $($process.ExitCode)" } } } } return $false } # Function to uninstall EXE-based installations function Remove-SlackEXE { Write-Host "Checking for EXE-based Slack installations..." $uninstallKeys = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' ) $foundAny = $false foreach ($keyPath in $uninstallKeys) { $keys = Get-ChildItem -Path $keyPath -ErrorAction SilentlyContinue | ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } foreach ($key in $keys) { if ($key.DisplayName -like "*Slack*") { $foundAny = $true Write-Host "Found: $($key.DisplayName) at $($keyPath)" if ($key.UninstallString) { # Extract the executable path and arguments $uninstallString = $key.UninstallString $exePath = "" $arguments = "" if ($uninstallString -match '^"([^"]+)"(.*)') { $exePath = $matches[1] $arguments = $matches[2].Trim() } elseif ($uninstallString -match '^([^\s]+)(.*)') { $exePath = $matches[1] $arguments = $matches[2].Trim() } Write-Host "Uninstall executable: $exePath" # For Slack, common silent parameters $silentParams = @( "--uninstall --force-uninstall", "--uninstall", "/S", "/SILENT", "-s" ) # First try QuietUninstallString if available if ($key.QuietUninstallString) { Write-Host "Trying QuietUninstallString..." $process = Start-Process -FilePath "cmd.exe" -ArgumentList "/c `"$($key.QuietUninstallString)`"" -Wait -PassThru -NoNewWindow if ($process.ExitCode -eq 0) { Write-Host "Successfully uninstalled using QuietUninstallString" return $true } } # Try each silent parameter foreach ($param in $silentParams) { Write-Host "Trying with parameters: $param" try { $fullArgs = if ($arguments) { "$arguments $param" } else { $param } $process = Start-Process -FilePath $exePath -ArgumentList $fullArgs -Wait -PassThru -NoNewWindow -ErrorAction Stop if ($process.ExitCode -eq 0) { Write-Host "Successfully uninstalled with parameters: $param" return $true } else { Write-Host "Exit code: $($process.ExitCode)" } } catch { Write-Host "Error: $_" } } } } } } if (-not $foundAny) { Write-Host "No EXE-based Slack installations found in registry" } return $false } # Function to kill Slack processes function Stop-SlackProcesses { Write-Host "Checking for running Slack processes..." $processes = Get-Process -Name "Slack*" -ErrorAction SilentlyContinue if ($processes) { Write-Host "Found $($processes.Count) Slack process(es). Attempting to stop..." foreach ($proc in $processes) { try { $proc | Stop-Process -Force -ErrorAction Stop Write-Host "Stopped process: $($proc.ProcessName) (PID: $($proc.Id))" } catch { Write-Host "Failed to stop process: $($proc.ProcessName) - $_" } } Start-Sleep -Seconds 2 } } # Function to clean up Slack folders function Remove-SlackFolders { Write-Host "Cleaning up Slack folders..." $foldersToRemove = @( "$env:LOCALAPPDATA\Slack", "$env:APPDATA\Slack", "$env:ProgramFiles\Slack", "${env:ProgramFiles(x86)}\Slack" ) foreach ($folder in $foldersToRemove) { if (Test-Path $folder) { Write-Host "Removing folder: $folder" try { Remove-Item -Path $folder -Recurse -Force -ErrorAction Stop Write-Host "Successfully removed: $folder" } catch { Write-Host "Failed to remove folder: $_" } } } } # Main uninstallation logic try { # Stop Slack processes first Stop-SlackProcesses # Try MSI uninstallation first $msiResult = Remove-SlackMSI if ($msiResult) { $uninstalled = $true Write-Host "Slack uninstalled via MSI method" } # Try EXE uninstallation $exeResult = Remove-SlackEXE if ($exeResult) { $uninstalled = $true Write-Host "Slack uninstalled via EXE method" } # Clean up folders regardless of uninstall method success Remove-SlackFolders # Final verification Start-Sleep -Seconds 3 $remainingInstalls = @( Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Slack*" } Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Slack*" } Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Slack*" } ) if ($remainingInstalls.Count -eq 0) { Write-Host "Verification: No Slack installations found in registry" $exitCode = 0 } elseif ($uninstalled) { Write-Host "Warning: Some Slack registry entries remain, but uninstallation was attempted" $exitCode = 0 } else { Write-Host "Error: Slack uninstallation failed" $exitCode = 1 } } catch { Write-Host "Critical error during uninstallation: $_" $exitCode = 1 } Write-Host "Slack uninstallation script completed with exit code: $exitCode" Exit $exitCode - name: Uninstall Zoom platform: windows description: Removes Zoom from a Windows device. script: | # Zoom Uninstall Script for Fleet $exitCode = 0 # Kill Zoom processes Get-Process -Name "Zoom*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 # Method 1: Try CleanZoom.exe if provided with the script $cleanZoom = "$PSScriptRoot\CleanZoom.exe" if (Test-Path $cleanZoom) { Write-Host "Using CleanZoom.exe" & $cleanZoom /silent $exitCode = $LASTEXITCODE Exit $exitCode } # Method 2: Registry uninstall (both HKLM and HKCU) $paths = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' ) $found = $false foreach ($path in $paths) { if (Test-Path $path) { Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Zoom*" } | ForEach-Object { $found = $true Write-Host "Uninstalling: $($_.DisplayName)" # Try QuietUninstallString if ($_.QuietUninstallString) { cmd /c "$($_.QuietUninstallString)" 2>&1 | Out-Null if ($LASTEXITCODE -eq 0) { $exitCode = 0; return } } # Try UninstallString with silent flags if ($_.UninstallString) { # Zoom-specific silent uninstall cmd /c "$($_.UninstallString) /uninstall /silent" 2>&1 | Out-Null if ($LASTEXITCODE -eq 0) { $exitCode = 0; return } # Generic silent cmd /c "$($_.UninstallString) /S" 2>&1 | Out-Null if ($LASTEXITCODE -eq 0) { $exitCode = 0; return } } } } } if (-not $found) { Write-Host "Zoom not found" $exitCode = 0 # Not found = success for Fleet } Exit $exitCode # # ╦ ╦╔╗╔╦ ╦═╗ ╦ # ║ ║║║║║ ║╔╩╦╝ # ╩═╝╩╝╚╝╚═╝╩ ╚═ - name: Uninstall fleetd platform: linux description: Unintalls the fleetd agent on a Linux device. script: | #!/bin/bash if [ $(id -u) -ne 0 ]; then echo "Please run as root" exit 1 fi function remove_fleet { set -x systemctl stop orbit.service || true systemctl disable orbit.service || true rm -rf /var/lib/orbit /opt/orbit /var/log/orbit /usr/local/bin/orbit /etc/default/orbit /usr/lib/systemd/system/orbit.service # Remove any package references if command -v dpkg > /dev/null; then dpkg --purge fleet-osquery || true elif command -v rpm > /dev/null; then rpm -e fleet-osquery || true fi # Kill any running Fleet processes pkill -f fleet-desktop || true # Reload systemd configuration systemctl daemon-reload echo "Fleetd has been successfully removed from the system." } if [ "$1" = "remove" ]; then # We are in the detached child process # Give the parent process time to report the success before removing echo "inside remove process" >>/tmp/fleet_remove_log.txt sleep 15 # We are root remove_fleet >>/tmp/fleet_remove_log.txt 2>&1 else # We are in the parent shell, start the detached child and return success echo "Removing fleetd, system will be unenrolled in 15 seconds..." echo "Executing detached child process" # We are root bash -c "bash $0 remove >/dev/null 2>/dev/null /etc/nftables.conf systemctl enable nftables - name: Enable auditd service platform: linux description: Enable and start the auditd service to provide system auditing and logging of security-relevant events. script: | #!/bin/bash systemctl --now enable auditd - name: Set default inactivity period for user accounts platform: linux description: Configure the default number of days (30) after a password expires before a user account is disabled. script: | #!/bin/bash useradd -D -f 30 - name: Set ownership and permissions on GRUB configuration platform: linux description: Configure /boot/grub/grub.cfg to be owned by root and inaccessible to non-privileged users. script: | #!/bin/bash chown root:root /boot/grub/grub.cfg chmod og-rwx /boot/grub/grub.cfg