Similarly with Windows endpoints, in Defender for Endpoint for Linux, one of the key states to understand about your endpoints is, the offboarding state. In this state, machines are no longer reporting MDE telemetry to Defender portal and if another AV/AM solution is in place, the Defender Antivirus component needs to be switched to a passive mode to prevent performance issues at the endpoint if two AV solutions are running in active mode.
There are certain instances when a machine or machines are offboarded that the corresponding status takes an unusual amount of time to report in the Defender portal.
The status that is shown in the portal is “Can be onboarded,” however this status doesn’t clarify with absolute certainty that the machine was offboarded from the platform. The status “Can be onboarded” means that either the endpoint was offboarded from the platform or, that it is a new device discovered by the “Device Discovery” service of MDE and the platform is highlighting this for you as something to address and cover the security gap that represents an endpoint without protection.
Figure 1.”Can be onboarded” status of a device.
In advanced hunting in the Defender portal, there’s not a direct field that reflects if a device was offboarded but if it is onboarded that is visible using a KQL query.
If a device is onboarded, this is shown in the portal, under Assets à Device Inventory à All devices, but not if it is offboarded.
Figure 2. Onboarding status as seen in the Device Inventory view.
To circumvent this existing challenge, I am using a similar approach to what I described in the article “Determine offboarding state using Powershell” and you can run a bash script that determines with a high level of confidence that a device was offboarded from the platform without the need to wait for the 7 days period it takes a device to be deemed as inactive in the console. This script can be handy in situations where offboarding is happening on devices that are experiencing communication issues or sensor issues and offboarding is the option to fix the problem and you would like to know straight away if the process was successful.
In essence, the bash script checks three indicators present in Linux system to confirm if the key services responsible for sending telemetry to the portal are up and running. The key parameters are listed below:
ü Confirm the onboard file exists in this path:
o "/etc/opt/microsoft/mdatp/mdatp_onboard.json"
ü Check the Defender daemon process is running: wdavdaemon/mdatp
ü Check mdatp rpm package is installed
High level execution of the script:
Scans computer(s) to determine onboarding method in MDE:
The script proceeds through several logical steps:
- It first checks if the onboarding file exists and attempts to extract the organization ID from it using either jq or a portable grep/awk fallback.
- Next, it determines if the MDE package is installed by querying the system’s package manager.
- It then checks if the Defender service is running, using systemd if available, or falling back to process checks otherwise.
- The script also locates the Defender CLI and, if present, queries its health status and extracts additional metadata such as org ID, machine ID, health state, and app version.
- In theory, to make it scalable and reach to more devices, this bash script could be pushed to Linux endpoints using any of the Linux management tools like Chef, Ansible or Puppet.
Finally, the script synthesizes these findings to decide the overall status: it reports “ONBOARDED” if the Defender agent is healthy, the service is active, or both the package and onboarding file are present; otherwise, it reports “OFFBOARDED.” The output summarizes key findings, making it easy for administrators or automation systems to quickly assess the Defender state on Linux endpoints.
For further troubleshooting with Linux devices:
Parameters used in the script:
The script contains different parameters to evaluate with a great level of certainty if a device was onboarded using one of the known methods:
- ONBOARD_FILE: The path to the Defender onboarding artifact (/etc/opt/microsoft/mdatp/mdatp_onboard.json). Its presence and contents are used to determine onboarding state and extract the organization ID.
- onboard_state: Indicates whether the onboarding file is present ("Present") or missing ("Missing").
- org_from_file / org_from_cli: The organization (tenant) ID, extracted from the onboarding file or from the Defender CLI health output, respectively.
- pkg_status: Reflects whether the Defender package (mdatp) is installed, and via which package manager (Installed(rpm), Installed(dpkg), or NotInstalled(...)).
- svc_state: The running state of the Defender service or process (active, inactive, UnitMissing, NoSystemd, etc.), determined via systemd or process checks.
- MDATP_CLI: The path to the Defender CLI binary, used to query health and metadata.
- healthy: Indicates if the Defender agent is healthy (true/false), as reported by the CLI.
- edr_machine_id: The unique machine ID assigned by Defender, extracted from the CLI health output.
- app_version: The installed version of the Defender agent, also from the CLI health output.
- overall: The final computed status, either "ONBOARDED" or "OFFBOARDED", based on the combination of health, service, package, and onboarding file checks.
- HOW TO RUN - EXAMPLE
- Open a terminal window in the Linux device you want to check and navigate to the directory where the script is saved.
- Run the following command to make it executable:
chmod +x MDE_CheckOffboardingState_Linux.sh
- Run the Script with Sufficient Privileges:
Sudo bash ./MDE_CheckOffboardingState_Linux.sh
Check the output of the script. If the endpoint is onboarded you will see an output similar to this:
Fig. 3 Linux device is onboarded in Defender for Endpoint
If the endpoint hasn’t been onboarded to Defender for Endpoint or it has been offboarded, the output of the shell script will be similar to this:
Fig. 4 A Linux device that is offboarded or never onboarded in Defender for Endpoint
Linux shell scripting code:
Code disclaimer:
The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.
NOTE: When using your scripting editing tool of choice, be aware of any additional spaces added as a result of the copy/past operation into your editing tool.
#!/usr/bin/env bash
# Check Microsoft Defender for Endpoint (MDE) offboarding/onboarding status on Linux
# v3 - robust unit detection, non-systemd fallback, smarter CLI/Org ID parsing
# Output is ASCII-only, suitable for CI/logging.
# USAGE DISCLAIMER
# The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are
# provided AS IS without warranty of any
# kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of
# merchantability or of fitness for a
# particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation
# remains with you. In no event shall
# Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for
# any damages whatsoever (including,
# without limitation, damages for loss of business profits, business interruption, loss of business information, or other # pecuniary loss) arising out of the use # of or inability to use the sample scripts or documentation, even if Microsoft
# has been advised of the possibility of such damages.
# Author: Edgar Parra - Microsoft v1.0
set -euo pipefail
ONBOARD_FILE="/etc/opt/microsoft/mdatp/mdatp_onboard.json"
# ---- helper: trim ----
_trim() { sed -e 's/^[[:space:]]\+//' -e 's/[[:space:]]\+$//' ; }
# ---- 1) Onboarding artifact check + Org ID from file (fallback) ----
onboard_state="Missing"
org_from_file="N/A"
if [[ -s "$ONBOARD_FILE" ]]; then
onboard_state="Present"
# Prefer jq if available, else portable grep/awk
if command -v jq >/dev/null 2>&1; then
org_from_file="$(jq -r '.. | .org_id? // .orgId? // .tenantId? // empty' "$ONBOARD_FILE" 2>/dev/null | head -n1 | _trim || true)"
[[ -z "${org_from_file}" || "${org_from_file}" == "null" ]] && org_from_file="N/A"
else
org_from_file="$(grep -oE '"(org_id|orgId|tenantId)"\s*:\s*"[^"]+"' "$ONBOARD_FILE" 2>/dev/null | head -n1 | awk -F\" '{print $4}' | _trim || true)"
[[ -z "${org_from_file}" ]] && org_from_file="N/A"
fi
fi
# ---- 2) Package presence (rpm or dpkg) ----
pkg_status="Unknown"
if command -v rpm >/dev/null 2>&1; then
if rpm -q mdatp >/dev/null 2>&1; then
pkg_status="Installed(rpm)"
else
pkg_status="NotInstalled(rpm)"
fi
elif command -v dpkg >/dev/null 2>&1; then
if dpkg -s mdatp >/dev/null 2>&1; then
pkg_status="Installed(dpkg)"
else
pkg_status="NotInstalled(dpkg)"
fi
fi
# ---- 3) Service/process state (robust) ----
svc_state="Unknown"
is_systemd_pid1="no"
if [[ -d /run/systemd/system ]] && [[ "$(cat /proc/1/comm 2>/dev/null)" == "systemd" ]]; then
is_systemd_pid1="yes"
fi
if [[ "$is_systemd_pid1" == "yes" ]] && command -v systemctl >/dev/null 2>&1; then
# Ask systemd directly about the unit
load_state="$(systemctl show mdatp -p LoadState --value 2>/dev/null || true)"
if [[ "$load_state" == "loaded" ]]; then
svc_state="$(systemctl is-active mdatp 2>/dev/null || true)"
else
# Some distros/versions resolve via status even if LoadState != loaded
if systemctl status mdatp --no-pager >/dev/null 2>&1; then
svc_state="$(systemctl is-active mdatp 2>/dev/null || true)"
else
svc_state="UnitMissing"
fi
fi
else
# No systemd as PID 1; use a process check
if pgrep -f "(wdavdaemon|mdatp)" >/dev/null 2>&1; then
svc_state="active"
else
svc_state="NoSystemd"
fi
fi
# ---- 4) CLI location + health (authoritative) ----
MDATP_CLI="$(command -v mdatp 2>/dev/null || true)"
[[ -z "$MDATP_CLI" ]] && MDATP_CLI="/opt/microsoft/mdatp/bin/mdatp"
org_from_cli="N/A"
edr_machine_id="N/A"
healthy="N/A"
app_version="N/A"
if [[ -x "$MDATP_CLI" ]]; then
# Use a short timeout if available, to avoid hangs
if command -v timeout >/dev/null 2>&1; then
health_json="$(timeout 6s "$MDATP_CLI" health -j 2>/dev/null || true)"
else
health_json="$("$MDATP_CLI" health -j 2>/dev/null || true)"
fi
if [[ -n "${health_json:-}" ]]; then
# Parse minimal fields without jq (portable)
org_from_cli="$(printf '%s' "$health_json" | grep -oE '"org_id"\s*:\s*"[^"]+"' | head -n1 | awk -F\" '{print $4}' | _trim || true)"
[[ -z "$org_from_cli" ]] && org_from_cli="N/A"
edr_machine_id="$(printf '%s' "$health_json" | grep -oE '"edr_machine_id"\s*:\s*"[^"]+"' | head -n1 | awk -F\" '{print $4}' | _trim || true)"
[[ -z "$edr_machine_id" ]] && edr_machine_id="N/A"
healthy="$(printf '%s' "$health_json" | grep -oE '"healthy"\s*:\s*(true|false)' | head -n1 | awk -F': ' '{print $2}' | tr -d ',' | _trim || true)"
[[ -z "$healthy" ]] && healthy="N/A"
app_version="$(printf '%s' "$health_json" | grep -oE '"app_version"\s*:\s*"[^"]+"' | head -n1 | awk -F\" '{print $4}' | _trim || true)"
[[ -z "$app_version" ]] && app_version="N/A"
fi
fi
# ---- 5) Decide overall status ----
# Consider ONBOARDED if:
# - healthy == true (authoritative), OR
# - service/process active, OR
# - package installed AND onboarding file present
overall="OFFBOARDED"
if [[ "$healthy" == "true" ]] || [[ "$svc_state" == "active" ]] || \
{ [[ "$pkg_status" == Installed* ]] && [[ "$onboard_state" == "Present" ]]; }; then
overall="ONBOARDED"
fi
# ---- 6) Output (ASCII-only, human-friendly) ----
echo "Status: $overall"
echo "Onboarding file: $onboard_state ($ONBOARD_FILE)"
echo "Package: $pkg_status"
echo "Service/Process: $svc_state"
# echo "Org ID (CLI): $org_from_cli"
# echo "Org ID (File): $org_from_file"
# echo "EDR Machine ID: $edr_machine_id"
# echo "App Version: $app_version"
Conclusion
The purpose of this script is to be used as an alternative to discover faster if a device has been offboarded or never onboarded in Defender for Endpoint.
It provides a swift approach for when deployments occur and you need to know faster when a device has been offboarded. It streamlines your deployment and allows you to move faster.
We understand that waiting 7 days for a device to be marked as “Can be onboarded” in the Defender portal, feels like a long wait but the reasons for this are security. To prevent devices that have been offline for a period of time that may reconnect with Defender.
Additional benefit
An additional benefit of this script is that it can also be run from Live Response console but only on devices that are onboarded in Defender for Endpoint. For devices that are not onboarded, Live Response is not available. However, running the script will tell you if the device is properly onboarded and the output of the script will be shown in the Live Response console. This is a practical way to run the script remotely without the need to logon to the device directly and find out quickly if the device was onboarded properly.
Fig. 5 Running the shell script from the Live Response console and output is shown in the Live Response console.