Skip to main content

🛡️ Check MS Defender

PyPI version Python versions License: MIT Build Publish Docstring coverage Quality Gate Status Maintainability Rating Reliability Rating Security Rating Bugs Vulnerabilities Code Smells Technical Debt

A comprehensive Nagios plugin for monitoring Microsoft Defender for Endpoint API endpoints. Built with modern Python practices and designed for enterprise monitoring environments.

✨ Features

  • 🔐 Dual Authentication - Support for Client Secret and Certificate-based authentication
  • 🎯 Multiple Endpoints - Monitor onboarding status, last seen, vulnerabilities, products with CVEs, alerts, and machine details
  • 📊 Nagios Compatible - Standard exit codes and performance data output
  • 🏗️ Clean Architecture - Modular design with testable components
  • 🔧 Flexible Configuration - File-based configuration with sensible defaults
  • 📈 Verbose Logging - Multi-level debugging support
  • 🐍 Modern Python - Built with Python 3.10+ using type hints and async patterns

🚀 Quick Start

Installation

# Create virtual environment (recommended)
python -m venv /usr/local/libexec/nagios/check_msdefender
source /usr/local/libexec/nagios/check_msdefender/bin/activate

# Install from source
pip install git+https://github.com/lduchosal/check_msdefender.git

Basic Usage

# Check machine onboarding status
check_msdefender onboarding -d machine.domain.tld

# Check last seen (with custom thresholds)
check_msdefender lastseen -d machine.domain.tld -W 7 -C 30

# Check vulnerabilities
check_msdefender vulnerabilities -d machine.domain.tld -W 10 -C 100

# Check products with CVE vulnerabilities
check_msdefender products -d machine.domain.tld -W 5 -C 1

# Check alerts
check_msdefender alerts -d machine.domain.tld -W 1 -C 5

# Check incidents (correlated alert groups)
check_msdefender incidents -d machine.domain.tld -W 1 -C 5

# List all machines
check_msdefender machines

# Get detailed machine info
check_msdefender detail -d machine.domain.tld

📋 Available Commands

Command Description Default Thresholds
onboarding Check machine onboarding status W:1, C:2
lastseen Days since machine last seen W:7, C:30
vulnerabilities Vulnerability score calculation W:10, C:100
products Count of vulnerable software with CVEs W:5, C:1
alerts Count of unresolved alerts W:1, C:0
incidents Count of unresolved incidents (correlated alerts) W:1, C:0
machines List all machines W:10, C:25
detail Get detailed machine information -

Vulnerability Scoring

The vulnerability score is calculated as:

  • Critical vulnerabilities × 100
  • High vulnerabilities × 10
  • Medium vulnerabilities × 5
  • Low vulnerabilities × 1

Products CVE Monitoring

The products command monitors installed software with known CVE vulnerabilities:

  • Groups CVEs by software (name, version, vendor)
  • Shows CVE details including severity levels and disk paths
  • Counts vulnerable software (not individual CVEs)
  • Default thresholds: Warning at 5 vulnerable software, Critical at 1
  • Displays up to 10 software entries with first 5 CVEs per software

--verify-paths: dropping what the host no longer carries

Microsoft's SoftwareVulnerabilitiesByMachine export is a snapshot regenerated on Microsoft's own cadence, so an uninstalled product keeps being reported for days, and so does a product that has already been patched. The export carries its own evidence: the disk and registry paths where each product was seen.

With --verify-paths, the check submits those paths to the machine in a single remote command and drops every product the host no longer confirms:

  • removed - none of the reported paths exists any more
  • upgraded - the file is there, but in a version above the one reported

Everything the host cannot answer for keeps its score: a path that could not be read (DENIED), an unreachable drive or share, a product the export gave no path for, or a probe that failed altogether. A failed probe never turns the check green - the raw score is what gets compared to the thresholds, and the output says so.

The rule is per product, not per path: one copy still on disk in the reported version keeps the whole product counted, however many other copies are gone. A leftover binary that exists but can never run (another CPU architecture, say) is an exception to register, not a stale entry.

The first line always says that the host was asked, even when nothing was excluded, and every path listed under a product carries the host's answer, the paths that keep it in the score first:

2 vulnerable products, score: 547 (raw 712, 1 stale excluded: 165), path verification: 27 paths, 5 absent, 0 unreadable

python 3.12.9.0 (python) - Score: 442, ...
 - [PRESENT 3.12.9] c:\program files\...\rust-lldb\win\aarch64\bin\python.exe
 - [ABSENT] c:\program files\...\rust-lldb\win\x64\bin\python.exe

Excluded products stay visible under Stale entries excluded, with the reason and the path that decided it, and the perfdata keeps the raw curve:

products=3319;500;2000 raw=6743 stale=3424 unverified=2

The probe itself is a command template, so the check is not tied to any one transport:

[verify]
# {host} is replaced by the machine's DNS name; the template is split shell-style and
# run without a shell. Default: ssh to the Nagios command account.
command = ssh -o BatchMode=yes -o ConnectTimeout=5 -l nagioscmd {host} powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File c:/programdata/nagioscmd/scripts/test_paths.ps1
# Seconds; spent on top of the API calls, so keep the sum under the Nagios
# service_check_timeout. Default: 20
timeout = 20

The name dialed is the one Defender reports for the machine. When the monitoring server reaches it under a different name -- a Nagios host_name whose alias is the Defender name, say -- pass that name with --verify-host, or the probe fails on a host key it has never seen while every other check on that host works:

check_msdefender products -d $HOSTALIAS$ --verify-paths --verify-host $HOSTNAME$

Paths go in on stdin, one per line; the command answers one line per path:

STATE<TAB>VERSION<TAB>PATH

where STATE is PRESENT, ABSENT, DENIED or ERROR. A reference implementation for Windows hosts ships with the Arcantel winnagioscmd Ansible role as test_paths.ps1.

Alert Monitoring

The alerts command monitors unresolved security alerts for a machine:

  • Counts only unresolved alerts (status ≠ "Resolved")
  • Excludes informational alerts when critical/warning alerts exist
  • Shows alert details including creation time, title, and severity
  • Default thresholds: Warning at 1 alert, Critical at 0 (meaning any alert triggers warning)

Incident Monitoring

An incident is a group of correlated alerts that Microsoft Defender aggregates to describe a single attack. Each alert carries the incidentId of the incident it belongs to. The incidents command:

  • Queries the device-scoped alerts endpoint (/api/machines/{id}/alerts) so no alert is dropped behind a tenant-wide page-size cap
  • Counts distinct unresolved incidents (alerts grouped by incidentId, status ≠ "Resolved")
  • Surfaces the most severe alert of each incident in the output
  • Default thresholds: Warning at 1 incident, Critical at 0 (meaning any incident triggers warning)

Onboarding Status Values

  • 0 - Onboarded ✅
  • 1 - InsufficientInfo ⚠️
  • 2 - Unknown ❌

⚙️ Configuration

Authentication Setup

Create check_msdefender.ini in your Nagios directory or current working directory:

Client Secret Authentication

[auth]
client_id = your-application-client-id
client_secret = your-client-secret
tenant_id = your-azure-tenant-id

[settings]
timeout = 30

Certificate Authentication

[auth]
client_id = your-application-client-id
tenant_id = your-azure-tenant-id
certificate_path = /path/to/certificate.pem
private_key_path = /path/to/private_key.pem

[settings]
timeout = 30

Transient API errors

Throttling (429) and transient server errors (500/502/503/504) are retried up to 3 attempts with backoff, honouring Retry-After capped at 5 s, so a passing Microsoft outage no longer turns into an UNKNOWN that lasts until the next retry. Timeouts are not retried, to keep the check inside Nagios' service_check_timeout. Once the retries are spent the check reports a single line, e.g. UNKNOWN: MS Defender API 503 Service Unavailable after 3 attempts: GET ….

Microsoft Defender API Setup

  1. Register Application in Azure Active Directory
  2. Grant API Permissions:
    • Machine.Read.All
    • Vulnerability.Read
    • Vulnerability.Read.All
    • Alert.Read.All
  3. Create Authentication (Secret or Certificate)
  4. Note Credentials (Client ID, Tenant ID, Secret/Certificate)

📚 Complete API Setup Guide

🔧 Command Line Options

Option Description Example
-c, --config Configuration file path -c /custom/path/config.ini
-m, --machineId Machine ID (GUID) -m "12345678-1234-1234-1234-123456789abc"
-d, --computerDnsName Computer DNS Name (FQDN) -d "server.domain.com"
-W, --warning Warning threshold -W 10
-C, --critical Critical threshold -C 100
-v, --verbose Verbosity level -v, -vv, -vvv
--version Show version --version

🏢 Nagios Integration

Command Definitions

# Microsoft Defender Commands
define command {
    command_name    check_defender_onboarding
    command_line    $USER1$/check_msdefender/bin/check_msdefender onboarding -d $HOSTALIAS$
}

define command {
    command_name    check_defender_lastseen
    command_line    $USER1$/check_msdefender/bin/check_msdefender lastseen -d $HOSTALIAS$ -W 7 -C 30
}

define command {
    command_name    check_defender_vulnerabilities
    command_line    $USER1$/check_msdefender/bin/check_msdefender vulnerabilities -d $HOSTALIAS$ -W 10 -C 100
}

define command {
    command_name    check_defender_products
    command_line    $USER1$/check_msdefender/bin/check_msdefender products -d $HOSTALIAS$ -W 5 -C 1
}

define command {
    command_name    check_defender_alerts
    command_line    $USER1$/check_msdefender/bin/check_msdefender alerts -d $HOSTALIAS$ -W 1 -C 5
}

Service Definitions

# Microsoft Defender Services
define service {
    use                     generic-service
    service_description     DEFENDER_ONBOARDING
    check_command           check_defender_onboarding
    hostgroup_name          msdefender
}

define service {
    use                     generic-service
    service_description     DEFENDER_LASTSEEN
    check_command           check_defender_lastseen
    hostgroup_name          msdefender
}

define service {
    use                     generic-service
    service_description     DEFENDER_VULNERABILITIES
    check_command           check_defender_vulnerabilities
    hostgroup_name          msdefender
}

define service {
    use                     generic-service
    service_description     DEFENDER_PRODUCTS
    check_command           check_defender_products
    hostgroup_name          msdefender
}

define service {
    use                     generic-service
    service_description     DEFENDER_ALERTS
    check_command           check_defender_alerts
    hostgroup_name          msdefender
}

🏗️ Architecture

This plugin follows clean architecture principles with clear separation of concerns:

check_msdefender/
├── 📁 cli/                     # Command-line interface
│   ├── commands/               # Individual command handlers
│   │   ├── onboarding.py      # Onboarding status command
│   │   ├── lastseen.py        # Last seen command
│   │   ├── vulnerabilities.py # Vulnerabilities command
│   │   ├── products.py        # Products CVE monitoring command
│   │   ├── alerts.py          # Alerts monitoring command
│   │   ├── machines.py        # List machines command
│   │   └── detail.py          # Machine detail command
│   ├── decorators.py          # Common CLI decorators
│   └── handlers.py            # CLI handlers
├── 📁 core/                    # Core business logic
│   ├── auth.py                # Authentication management
│   ├── config.py              # Configuration handling
│   ├── defender.py            # Defender API client
│   ├── exceptions.py          # Custom exceptions
│   ├── nagios.py              # Nagios plugin framework
│   └── logging_config.py      # Logging configuration
├── 📁 services/                # Business services
│   ├── onboarding_service.py  # Onboarding business logic
│   ├── lastseen_service.py    # Last seen business logic
│   ├── vulnerabilities_service.py # Vulnerability business logic
│   ├── products_service.py    # Products CVE monitoring business logic
│   ├── alerts_service.py      # Alerts monitoring business logic
│   ├── machines_service.py    # Machines business logic
│   ├── detail_service.py      # Detail business logic
│   └── models.py              # Data models
└── 📁 tests/                   # Comprehensive test suite
    ├── unit/                   # Unit tests
    ├── integration/            # Integration tests
    └── fixtures/               # Test fixtures

Key Design Principles

  • 🎯 Single Responsibility - Each module has one clear purpose
  • 🔌 Dependency Injection - Easy testing and mocking
  • 🧪 Testable - Comprehensive test coverage
  • 📈 Extensible - Easy to add new commands and features
  • 🔒 Secure - No secrets in code, proper credential handling

🧪 Development

Development Setup

# Clone repository
git clone https://github.com/lduchosal/check_msdefender.git
cd check_msdefender

# Create development environment
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

# Install in development mode
pip install -e .

Code Quality Tools

# Format code
black check_msdefender/

# Lint code
flake8 check_msdefender/

# Type checking
mypy check_msdefender/

# Run tests
pytest tests/ -v --cov=check_msdefender

Building & Publishing

# Build package
python -m build

# Test installation
pip install dist/*.whl

# Publish to PyPI
python -m twine upload dist/*

🔍 Output Examples

Successful Check

DEFENDER OK - Onboarding status: 0 (Onboarded) | onboarding=0;1;2;0;2

Warning State

DEFENDER WARNING - Last seen: 10 days ago | lastseen=10;7;30;0;

Critical State

DEFENDER CRITICAL - Vulnerability score: 150 (1 Critical, 5 High) | vulnerabilities=150;10;100;0;

Alerts Warning

DEFENDER WARNING - Unresolved alerts for machine.domain.com | alerts=2;1;5;0;
Unresolved alerts for machine.domain.com
2025-09-14T10:22:14.12Z - Suspicious activity detected (New high)
2025-09-14T12:00:00.00Z - Malware detection (InProgress medium)

🔧 Troubleshooting

Common Issues

Issue Solution
Authentication Errors Verify Azure app permissions and credentials
Network Connectivity Check firewall rules for Microsoft endpoints
Import Errors Ensure all dependencies are installed
Configuration Issues Validate config file syntax and paths

Debug Mode

Enable verbose logging for detailed troubleshooting:

# Maximum verbosity
check_msdefender vulnerabilities -d machine.domain.tld -vvv

# Check specific configuration
check_msdefender onboarding -c /path/to/config.ini -d machine.domain.tld -vv

Required Network Access

Ensure connectivity to:

  • login.microsoftonline.com
  • api.securitycenter.microsoft.com
  • api-eu.securitycenter.microsoft.com
  • api-eu3.securitycenter.microsoft.com
  • api-uk.securitycenter.microsoft.com

📊 Exit Codes

Code Status Description
0 OK Value within acceptable range
1 WARNING Value exceeds warning threshold
2 CRITICAL Value exceeds critical threshold
3 UNKNOWN Error occurred during execution

🤝 Contributing

We welcome contributions! Here's how to get started:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Guidelines

  • Follow PEP 8 style guide
  • Add tests for new features
  • Update documentation as needed
  • Ensure all tests pass before submitting

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments


💖 Sponsor

If this project helps you, consider supporting its development:

GitHub Sponsors

Metadata

Release files for check-msdefender 1.4.19

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for check-msdefender 1.4.19
File Size Uploaded
check_msdefender-1.4.19.tar.gz 69.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for check-msdefender 1.4.19
File Interpreter ABI Platform
check_msdefender-1.4.19-py3-none-any.whl Python 3 none any Details

Total release size: 123.5 kB

Release files / check_msdefender-1.4.19.tar.gz

Download URL check_msdefender-1.4.19.tar.gz
Size 69.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e259b262e2aa1f3fe91312dc9996950492e837cbac81e40ee30b1006ec40322f
BLAKE2b-256 checksum
How to use checksums
2ccce4b03d597049cd3e1de328fe7b4a477ed5e24b9db45b4175de74812e617a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.29.2 CPython/3.13.15 Darwin/25.6.0

Release files / check_msdefender-1.4.19-py3-none-any.whl

Download URL check_msdefender-1.4.19-py3-none-any.whl
Size 53.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
075996ffd0a78182ef92b37a4f704d632764dfc1fa29f71e8a7695add070a2f1
BLAKE2b-256 checksum
How to use checksums
ad13f1a70c3906b4c99fef11d19cfc4745e73b9ebd5b7a23a6319e339da1bc7d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.29.2 CPython/3.13.15 Darwin/25.6.0

Release history Release notifications | RSS feed

1.4.26

2 release files

1.4.21

2 release files

1.4.20

2 release files

This release

1.4.19 This release

2 release files

1.4.18

2 release files

1.4.17

2 release files

1.4.16

2 release files

1.4.15

2 release files

1.4.14

2 release files

1.4.12

2 release files

1.4.11

2 release files

1.4.10

2 release files

1.4.9

2 release files

1.4.8

2 release files

1.4.7

2 release files

1.4.5

2 release files

1.4.4

2 release files

1.4.3

2 release files

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.23

2 release files

1.2.22

2 release files

1.2.20

2 release files

1.2.19

2 release files

1.2.18

2 release files

1.2.16

2 release files

1.2.15

2 release files

1.2.14

2 release files

1.2.12

2 release files

1.2.11

2 release files

1.2.10

2 release files

1.2.8

2 release files

1.2.7

2 release files

1.2.6

2 release files

1.2.5

2 release files

1.2.4

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.1.17

2 release files

1.1.16

2 release files

1.1.15

2 release files

1.1.11

2 release files

1.1.10

2 release files

1.1.9

2 release files

1.1.8

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page