ldo xdr machines web01 web02 --all-records # onboarding, health, last seen, tags, device group
ldo xdr stale --older-than 30d # machines Defender has not seen lately
ldo xdr alerts --since 24h --severity medium
ldo xdr alerts --device web01 --include-resolved
ldo xdr vulns web01 --severity high
ldo xdr indicators # custom indicators of compromiseldo xdr hunt "DeviceInfo | take 10"
ldo xdr hunt "EmailEvents | where Timestamp > ago(1d) | take 50" --timespan 7d
ldo xdr hunt --file queries/failed-logons.kql -o csv > failed-logons.csv
ldo xdr hunt "DeviceInfo | take 10" --endpoint # the device tables, with the Azure CLI's sign-inxdr hunt (and graph hunt, the same thing) runs through Microsoft Graph, which covers every
Defender XDR table: devices, email, identity, cloud apps and alerts. It needs
ThreatHunting.Read.All, which the Azure CLI's token never has: use a profile with
your own app registration. --endpoint uses the
Defender for Endpoint API instead: device tables only, but your Defender role is enough.
ldo xdr timeline web01 --since 6h
ldo xdr timeline web01.corp.example --from 2026-09-24T09:00 --to 2026-09-24T12:30
ldo xdr timeline web01 --today --type process,network --endpoint
ldo xdr timeline web01 --since 2h -o csv > web01-timeline.csv
ldo xdr timeline web01 --type logon --show-query # the KQL, to run or change yourselfDefender has no API for the device timeline. The timeline page in the portal reads an
internal service of the portal's own, which Microsoft neither documents nor supports, and
which accepts no token an app registration or the Azure CLI can get. Tools that call it
borrow a browser's session, which ldo will not do.
What it can do is ask Advanced Hunting, which holds the events the timeline is drawn from,
in its device tables. xdr timeline runs one query over them for one device and one window
of time, and lists the events newest first, each as TIME, TYPE, ACTION, ACCOUNT, PROCESS and
DETAIL:
--type |
Table | DETAIL |
|---|---|---|
process |
DeviceProcessEvents |
the command line |
network |
DeviceNetworkEvents |
the remote address and port, and URL, or what is listening |
file |
DeviceFileEvents |
the file's path |
registry |
DeviceRegistryEvents |
the key, and the value set |
logon |
DeviceLogonEvents |
the logon type, and where from |
image-load |
DeviceImageLoadEvents |
the DLL or library loaded |
other |
DeviceEvents |
antivirus detections, exploit protection and the rest |
alert |
AlertEvidence and AlertInfo |
the alert's title, its severity as the ACTION |
How it differs from the portal's timeline:
- 30 days. Advanced Hunting keeps 30 days of device events; the portal reaches further back. A window older than that is warned about, since those events are not there.
- The newest 1000 by default. A query returns at most 100,000 rows, and each one counts
against the tenant's hunting quota. A busy server writes thousands of events an hour, so
narrow the window or
--typebefore you raise--limit; a timeline that stopped short says so. - Just the events. What the portal adds on top (events flagged on the timeline, grouped related events, techniques shown inline) is not in the tables.
- One name, maybe more than one device. A rebuilt server keeps its name and gets a new
id. When events come from more than one device, it says so, and
-o jsonhas each event'sdevice_id. A host name on its own (web01) also findsweb01.corp.example, neverweb010.
Times show in local time; -o json has them in UTC, as the tables keep them. Like xdr hunt, it goes through Graph (ThreatHunting.Read.All) unless you pass --endpoint, which
works with the Azure CLI's sign-in. The Defender for Endpoint API has the device tables but
not the alert ones, so with --endpoint alerts are left out (a note says so), and --type alert --endpoint is refused. --from and --to take a day, or a day and a time (local, or
UTC with a Z); --today, --yesterday and --since work too.
In the unified security operations platform, Sentinel's incidents land in the same queue as Defender's, so these read both:
ldo xdr incidents top # today's open incidents, most severe first
ldo xdr incidents top --severity high -n 20
ldo xdr incidents latest --source sentinel # the newest, any status, last 30 days
ldo xdr incidents list --from 2026-09-01 --to 2026-09-24
ldo xdr incidents list --from 2026-09-24T09:00 --to 2026-09-24T12:00 # or between times
ldo xdr incidents list --yesterday --status resolved
ldo xdr incidents list --since 6h --updated # updated, not created, in the last 6 hours
ldo xdr incidents summary --since 7d # counts by severity, status and source
ldo xdr incidents show 12345 # alerts, devices, users and the portal link| Option | Values |
|---|---|
| window | --today, --yesterday, --since 7d, or --from and --to (a day, YYYY-MM-DD, today or yesterday, whole; or a moment, YYYY-MM-DDTHH:MM, local or with a Z for UTC); --updated windows on the last update |
--status |
open, active, in-progress, awaiting-action, resolved, redirected, all (repeatable) |
--severity |
at least informational, low, medium or high |
--source |
sentinel, endpoint, identity, office, cloud-apps, cloud, xdr, entra, app-governance, dlp, insider-risk (repeatable) |
-n, --limit |
how many |
Incidents need SecurityIncident.Read.All (the Azure CLI's token never has it) and a role
such as Security Reader. A Sentinel workspace not onboarded to the Defender portal keeps its
incidents to itself: read its SecurityIncident table with ldo logs query.
ldo xdr detections list # every rule: status, schedule, severity, tactic
ldo xdr detections list --status autoDisabled # the ones Defender turned off itself
ldo xdr detections show "Certutil used to download remote content"
ldo xdr detections show 7506 --yaml > certutil.yaml
ldo xdr detections export ./custom-detections # every rule, as YAML files for Terraform
ldo xdr detections export ./backup --no-id # a backup that creates the rules anewWith Sentinel run from the Defender portal, detections live there as custom detection rules:
an Advanced Hunting query on a schedule, and the alert (and any automated response) it
raises. The old Sentinel REST API's analytics rules belong to the Azure portal it is leaving,
so ldo reads these through Microsoft Graph (security/rules/detectionRules), which is still
beta only.
What the API no longer says. On 1 October 2026 Microsoft removes the legacy properties
(isEnabled, detectorId, lastRunDetails and others), and with lastRunDetails goes the
only report of how each run went. ldo uses none of them. The sign left is a rule's
status: autoDisabled means Defender turned the rule off itself, usually after its query
failed again and again. list exits 3 when it finds one.
Export to YAML. export (and show --yaml for one rule) writes the analyst layout of
terraform-msgraph-xdr-custom-detection-rules:
one file per rule at TACTIC/RULE-NAME.yaml, the tactic being the rule's first, in kebab
case (command-and-control/), or uncategorised/. It is the conversion LibreDevOpsHelpers'
Export-LdoCustomDetectionRule does, and every file is checked in the tests against the
module's own schema:
- The rule's server id is kept, since the module keys rules by it, so
terraform importlines up.--no-idleaves it out, for a backup meant to create the rules anew. - A rule Defender turned off is written
disabled, since the schema allows only enabled and disabled, with aTODO(export)comment saying why and to fix its query first. - A rule written the old way (a schedule period, a category and technique list, impacted
assets, response actions) is converted where it can be; what cannot be is a
TODO(export)comment, never dropped. So is a rule that maps no entities, which the schema requires. - A rule carrying automated actions is noted, since the module call then needs
allow_automated_actions = true. - Files already there are kept unless
--force, and two rules whose names make the same file name both survive (the second gets its id added). Nothing is written through a link or outside the folder named, and the tenant is only read.
Rules need CustomDetection.Read.All on the Graph token. The Azure CLI cannot ask for it, so
use a profile with your own app registration,
or have an admin consent the delegated permission for the Azure CLI's own app
(04b07795-8ddb-461a-bbee-02f9e1bf7b46).
Microsoft's MDE Client Analyzer
checks one device's Defender for Endpoint (connectivity, services, configuration, versions)
and zips what it finds with everything it collected. xdr analyzer reads those zips, one or
many, and lists each device's findings, errors first:
ldo xdr analyzer MDEClientAnalyzerResult.zip # one device's findings
ldo xdr analyzer results/*.zip --severity warning # many: only warnings and errors
ldo xdr analyzer MDEClientAnalyzerResult.zip --guidance # with what to do about each
ldo xdr analyzer MDEClientAnalyzerResult.zip --facts # its OS, versions and services
ldo xdr analyzer results/*.zip -o json > analyzer.json # every result in fullIt takes the zip itself, the folder it unpacks to, or the results XML, from:
- Windows,
MDEClientAnalyzer.cmd: it readsSystemInfoLogs\MDEClientAnalyzer.xml, whose findings carry their own severity, check, result and guidance. - Linux and macOS, the support tool (
mde_support_tool.shor its compiledMDESupportTool): it readsmde.xml. Those findings are ids alone, soldosays what each means, or uses the tool's ownevents.xmlwhere the zip carries it.
Everything is read here: nothing is sent anywhere, and nothing in the zip is unpacked to disk (only the results file is read, up to 16 MB). Exits 3 when a device has an error or a warning.