🛡️ Check MS Defender
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 moreupgraded- 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.
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
Microsoft Defender API Setup
- Register Application in Azure Active Directory
- Grant API Permissions:
Machine.Read.AllVulnerability.ReadVulnerability.Read.AllAlert.Read.All
- Create Authentication (Secret or Certificate)
- Note Credentials (Client ID, Tenant ID, Secret/Certificate)
🔧 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.comapi.securitycenter.microsoft.comapi-eu.securitycenter.microsoft.comapi-eu3.securitycenter.microsoft.comapi-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:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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
- Built with nagiosplugin framework
- Uses Azure Identity SDK for authentication
- Powered by Click for CLI interface
⭐ Star this repository if you find it useful!
💖 Sponsor
If this project helps you, consider supporting its development:
Metadata
Release files for check-msdefender 1.4.18
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| check_msdefender-1.4.18.tar.gz | 63.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| check_msdefender-1.4.18-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 114.6 kB
Release files / check_msdefender-1.4.18.tar.gz
| Download URL | check_msdefender-1.4.18.tar.gz |
|---|---|
| Size | 63.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da45de53a5f13fc9d04ed54ef934aed182a8fa88494780291dcaf4f338de4592
|
|
BLAKE2b-256 checksum How to use checksums |
2fb9630cda98e45a59ddb001b450a70339326ace40403f558f9333a6f7e3a9d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
pdm/2.29.2 CPython/3.13.15 Linux/6.17.0-1022-azure
|
Release files / check_msdefender-1.4.18-py3-none-any.whl
| Download URL | check_msdefender-1.4.18-py3-none-any.whl |
|---|---|
| Size | 51.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1905110d418aa188e5d7326daa11915417d030dc359c1c75265b7491b38e3e5b
|
|
BLAKE2b-256 checksum How to use checksums |
8a5aee6c29d5eb4ae96d6dc36eb3b60e1854c90905f50c3a72aa7a97d56c4459
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
pdm/2.29.2 CPython/3.13.15 Linux/6.17.0-1022-azure
|