Build containers / fedora-cosmic (44) (pull_request) Canceled after 0s
Build containers / fedora-cosmic (rawhide) (pull_request) Canceled after 0s
Build containers / fedora-remote (44) (pull_request) Canceled after 0s
Build containers / fedora-intune (44) (pull_request) Canceled after 0s
Build containers / Compute changes (pull_request) Canceled after 14s
Build containers / Prune old releases and tags (pull_request) Skipped
The health snapshot's temp directory, chmod and mv in /run every 5 minutes showed up in Defender's timeline as "Data Staged" and "Executable permissions were added". Drop the snapshot service and timer. The compliance script now runs `mdatp health` through systemd-run, so systemd starts mdatp and Defender answers it even when the script runs inside himmelblaud-tasks. Without systemd-run access it asks mdatp directly, as before. No image support needed, so the script works on other distros as is. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
77 lines
2.7 KiB
Bash
Executable File
77 lines
2.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# Intune custom compliance discovery script for Linux: is Microsoft Defender
|
|
# for Endpoint (mdatp) installed, onboarded, licensed and protecting?
|
|
#
|
|
# Upload in Intune > Endpoint security > Device compliance > Scripts (Linux),
|
|
# and pair it with defender-compliance.json in a Linux compliance policy
|
|
# (Custom compliance). Himmelblau runs it as root inside
|
|
# himmelblaud-tasks.service and sends the JSON on stdout to Intune, which
|
|
# evaluates it against the rules. Every setting is always emitted, so a missing
|
|
# or stopped Defender reads as non-compliant instead of as a script error.
|
|
|
|
MDATP=/usr/bin/mdatp
|
|
|
|
# Defender won't answer `mdatp health` started from inside himmelblaud-tasks
|
|
# (it can't read that process while checking the caller), so ask through
|
|
# systemd, which runs mdatp as its own child. Elsewhere, ask mdatp directly.
|
|
health="$(systemd-run --quiet --wait --pipe --collect "${MDATP}" health 2>/dev/null)"
|
|
grep -q '^healthy' <<< "${health}" || health="$("${MDATP}" health 2>/dev/null)"
|
|
|
|
# Print one field of the `mdatp health` output ("name : value") without
|
|
# quotes or a trailing " [managed]".
|
|
field() {
|
|
grep -m1 "^$1 *:" <<< "${health}" \
|
|
| sed -e 's/^[^:]*: *//' -e 's/ \[managed\]$//' -e 's/^"//' -e 's/"$//'
|
|
}
|
|
|
|
bool() {
|
|
[[ "$1" == true ]] && echo true || echo false
|
|
}
|
|
|
|
installed=false
|
|
running=false
|
|
healthy=false
|
|
licensed=false
|
|
onboarded=false
|
|
rtp=false
|
|
behavior_monitoring=false
|
|
cloud_protection=false
|
|
definitions_ok=false
|
|
definitions_age=-1
|
|
version=""
|
|
|
|
if [[ -x "${MDATP}" ]]; then
|
|
installed=true
|
|
if [[ "$(field healthy)" =~ ^(true|false)$ ]]; then
|
|
# The daemon answered.
|
|
running=true
|
|
healthy="$(bool "$(field healthy)")"
|
|
licensed="$(bool "$(field licensed)")"
|
|
rtp="$(bool "$(field real_time_protection_enabled)")"
|
|
[[ "$(field behavior_monitoring)" == enabled ]] && behavior_monitoring=true
|
|
cloud_protection="$(bool "$(field cloud_enabled)")"
|
|
[[ -n "$(field org_id)" ]] && onboarded=true
|
|
[[ "$(field definitions_status)" == up_to_date ]] && definitions_ok=true
|
|
age="$(field definitions_updated_minutes_ago)"
|
|
[[ "${age}" =~ ^[0-9]+$ ]] && definitions_age=$(( age / 1440 ))
|
|
version="$(field app_version)"
|
|
version="${version//[^0-9A-Za-z._-]/}"
|
|
fi
|
|
fi
|
|
|
|
cat <<EOF
|
|
{
|
|
"DefenderInstalled": ${installed},
|
|
"DefenderRunning": ${running},
|
|
"DefenderHealthy": ${healthy},
|
|
"DefenderLicensed": ${licensed},
|
|
"DefenderOnboarded": ${onboarded},
|
|
"DefenderRealTimeProtection": ${rtp},
|
|
"DefenderBehaviorMonitoring": ${behavior_monitoring},
|
|
"DefenderCloudProtection": ${cloud_protection},
|
|
"DefenderDefinitionsUpToDate": ${definitions_ok},
|
|
"DefenderDefinitionsAgeDays": ${definitions_age},
|
|
"DefenderVersion": "${version}"
|
|
}
|
|
EOF
|