Skip to content

Get started

Requires Python 3.11+. pipx gives xdr its own isolated environment and puts the command on your PATH, which matters when an agent spawns it as a subprocess.

Terminal window
pipx install "git+https://github.com/NerfBlasters/m365-xdr-cli.git"
xdr --version
Terminal window
python -m pip install --user pipx
python -m pipx ensurepath
# refresh PATH in this session so pipx and xdr are visible without reopening
$env:Path = [Environment]::GetEnvironmentVariable("Path","User") + ";" + [Environment]::GetEnvironmentVariable("Path","Machine")
pipx install "git+https://github.com/NerfBlasters/m365-xdr-cli.git"
xdr --version
Terminal window
pipx upgrade xdr-cli # or: pipx reinstall xdr-cli

Developers: see Development for an editable install.

xdr uses Click’s shell completion. To enable it for the current Bash session:

Terminal window
eval "$(_XDR_COMPLETE=bash_source xdr)"

For Zsh use eval "$(_XDR_COMPLETE=zsh_source xdr)"; for Fish use _XDR_COMPLETE=fish_source xdr | source. To avoid generating the script on every shell startup, save that shell’s generated output and source the saved file from your shell configuration. PowerShell users can generate its script with $env:_XDR_COMPLETE = 'powershell_source'; xdr, then clear the variable with Remove-Item Env:_XDR_COMPLETE and load the saved script.

The former --install-completion and --show-completion options were removed in 0.15.0. Help now uses plain Click formatting; command examples and parameter help remain available through --help.

Portal cookie (quick start)Entra app registration
SetupMinutes: copy one request from your browser’s DevToolsRegister an app, add permissions, get admin consent
Talks toThe Defender portal’s own apiproxy interface — undocumented and unsupported by Microsoft; can break without noticeMicrosoft Graph security and Defender for Endpoint APIs — documented and supported
CredentialYour browser session cookie: a bearer credential for you, subject to the same expiry, Conditional Access and sign-in-frequency rules as the portal. Renewal is manual (re-import).MSAL tokens with refresh; or a service principal for automation
CoversHunting, incidents, alerts, investigate, domains, device show/timeline/action-status, every response action, package downloadEverything except package download and the AD domain inventory
PermissionsWhatever your user already has in the portal (Defender RBAC applies)Delegated permissions you grant, plus the user’s Defender roles

Start with the portal cookie if you want to try the tool today. Set up the app registration when you need a supported path, CI/automation, or a team-wide credential. Both can coexist; xdr picks the official backend when its credentials are present and the cookie otherwise (--backend overrides). Details, limits and error codes: docs/portal_cookie.md.

  1. Tell xdr which tenant you are in. Create ~/.xdr-cli/config.toml (or add the line to an existing one). The Directory (tenant) ID is on the Entra admin center’s Overview page, and in Defender portal URLs as tid=.

    tenant_id = "<TENANT_ID>"
  2. Capture your session, import it, and start investigating:

Terminal window
# 2. Capture your portal session (tested with Microsoft Edge):
# - sign in to https://security.microsoft.com and open any device's
# Timeline tab
# - open DevTools (F12) > Network, filter for "apiproxy"
# - right-click the timeline request > Copy > "Copy as cURL (bash)"
# - save the clipboard to a file, e.g. ~/mde-curl.txt
xdr auth portal-cookie ~/mde-curl.txt # stores the cookie (0600) and
# shreds the source file
xdr auth status # backend: portal-cookie
# 3. Look around.
xdr incidents list --since 7d --severity high
xdr incidents show <ID> --expand alerts # <ID> is .id from the list
# 4. Investigate one incident end to end.
xdr investigate --auto-enrich <ID>
# 5. Hunt.
xdr hunt run "DeviceProcessEvents | where Timestamp > ago(1d) | where FileName == 'powershell.exe' | take 10"
xdr library list --search kerberos
xdr library run qry_process_tree --param device_name=WS-01

Optional: xdr lists init copies the reference-list templates the hunting library reads (internal subnets, known-good signers, tenant domains, …) into ~/.xdr-cli/lists/. Generic lists ship with defaults; tenant-specific ones such as TenantDomains start empty. Not needed for incidents or investigate.

When a command answers with exit 2 in cookie mode, the session has expired: capture and import a fresh cookie. xdr auth logout deletes the local cookie only; it does not sign you out of the portal.

Global flags go before the subcommand: xdr --quiet incidents list, xdr --no-interactive investigate 42, xdr --backend official hunt run ….