Enterprise-grade Microsoft 365 security auditor for Managed Service Providers โ CLI and interactive terminal UI
- Overview
- Quick Start
- Terminal UI
- Project Status
- Architecture
- Control Library
- Usage Examples
- Configuration
- Development
- Testing
- Roadmap
- Contributing
M365 Sheppard Agent is a production-ready security auditing tool that systematically evaluates Microsoft 365 tenant security posture against 84 controls derived from industry best practices (CIS, NIST 800-53, ISO 27001, CoreView) plus external threat intelligence (OSINT).
- ๐ Comprehensive Coverage - 84 controls across 9 categories (security + business + OSINT)
- โก Real-time Evaluation - Direct Microsoft Graph API integration
- ๐ฏ ROI-Based Prioritization - Security impact รท implementation effort ranking
- ๐ฅ๏ธ Interactive Terminal UI - Browse controls, run audits, and triage findings without leaving the terminal
- ๐ Professional Reporting - JSON and HTML outputs for dashboards and compliance
- ๐ Secure by Design - AST-validated code execution, least-privilege permissions
- ๐ MSP-Ready - Multi-tenant support with parallel execution
- ๐ป CLI-First - Scriptable, automation-ready, CI/CD integration
Unlike vulnerability scanners or compliance tools, M365 Sheppard provides:
- Actionable Recommendations - Every finding includes remediation steps with complexity ratings (1-5)
- Intelligent Prioritization - ROI algorithm ranks fixes by security impact vs. effort
- MSP Operations - Built for managing multiple tenants at scale
- Open Architecture - YAML-based control definitions for easy customization
- Zero Dependencies - No agents, no special permissions beyond read-only Graph API
- Python 3.11+ (Download)
- Microsoft 365 tenant with appropriate licensing
- Azure AD app registration with Graph API permissions:
SecurityEvents.Read.AllOrganization.Read.AllPolicy.Read.ConditionalAccessUser.Read.AllDirectory.Read.All
# Clone the repository
git clone https://github.com/mstanton/m365-sheppard.git
cd m365-sheppard
# Install with uv (recommended - fastest)
uv sync
# Or install with pip
pip install -e .
# Verify installation
uv run sentinel --version
# Output: M365 Sheppard Agent v0.1.0# 1. Initialize configuration
uv run sentinel config init
# 2. Configure your tenant (edit ~/.m365sheppard/config.yaml)
# Add tenant details and authentication credentials
# 3. Set environment variables
export TENANT1_CLIENT_SECRET="your-secret-here"
# 4. Run your first audit
uv run sentinel audit run --tenant your-tenant-name
# 5. View results
cat reports/your-tenant-name_*_audit.json
# ...or do steps 4-5 interactively in the terminal UI
uv run sentinel tuiThe TUI is the primary interface for interactive work: browsing the control library, launching audits, and triaging findings โ all inside the terminal with a cyberpunk aesthetic. Launch it with:
uv run sentinel tui| Tab | What it does |
|---|---|
| 1 ยท Dashboard | Control library overview: totals per category, severity breakdown |
| 2 ยท Controls | Browse all 84 controls with live text/severity/category filtering and full detail (description, remediation, compliance) |
| 3 ยท Audit Run | Launch an audit against a configured tenant with per-category progress bars and a live status stream for every control |
| 4 ยท Findings | Load any audit report (latest is auto-detected) and triage findings sorted by severity |
| 5 ยท OSINT | Scan a tenant's users for breach exposure (HIBP) with per-account breach drill-down. Live-only โ full addresses are never written to disk |
Selecting a finding shows the full picture in a scrollable detail pane:
- Expected vs. current state โ the configuration gap at a glance
- Metrics & evidence โ the concrete data the control observed (affected accounts, policy names, DNS records, โฆ)
- Remediation plan โ numbered steps with per-step time estimates, complexity rating, total effort hours, and a direct admin-portal link
- Compliance mappings โ CIS / NIST / ISO references per finding
- Data-availability warning โ flagged when Graph permissions were missing, so partial results are never mistaken for clean ones
| Key | Action |
|---|---|
1-5 |
Jump to tab |
/ |
Focus the controls filter |
Ctrl+R |
Reset filters |
Ctrl+G |
Run audit |
j / k |
Move through tables |
? |
Help overlay |
q |
Quit |
Audits started from the TUI use the same AuditEngine as the CLI โ same
controls, same sandbox, same report format. When a run finishes, the report
is exported and loaded straight into the Findings tab.
What's Working:
- โ 84 YAML Control Files - Fully implemented across all 9 categories
- โ Graph API Integration - Real data collection from Microsoft 365
- โ Safe Evaluation Engine - AST-validated sandbox (imports, dunder access, and introspection builtins blocked)
- โ
Trusted OSINT Executors - Network-based checks (DNS lookups) run as reviewed in-repo code, dispatched by
check.type - โ Interactive Terminal UI - Textual-based dashboard, controls browser, audit runner, and findings triage
- โ JSON/HTML Reporting - Professional audit reports with metrics
- โ Configuration Management - YAML config with environment variable secrets
- โ Multi-Tenant Support - Parallel execution for MSP workflows
- โ Complexity Scoring - ROI ranking with 1-5 effort ratings
- โ Response Caching, Structured Logging, Scheduled Audits, Webhooks
- โ CLI Interface - Complete Typer-based command structure
- โ Unit Tests - 337 passing tests (engine, sandbox, scoring, OSINT, TUI)
Control Definitions: 84 YAML files
Categories Covered: 9 (IAM, ATP, DGV, DEV, APP, MON, OPS, BIZ, OSINT)
Graph API Endpoints: 20+
OSINT Integrations: DNS health checks (SPF/DKIM/DMARC), breach detection (HIBP)
Tests: 337 passing (core engine/controls well covered; TUI and Graph
client layers have targeted smoke coverage)
The agent has been run against live Microsoft 365 tenants and produces
complete audit reports with scored findings and remediation guidance.
Reports land in the configured reports/ directory (tenant data stays
local and is never committed).
m365-sheppard/
โโโ src/m365_sheppard/ # Core application code
โ โโโ cli/ # Typer-based CLI commands
โ โ โโโ main.py # App entry point
โ โ โโโ commands/ # Audit, config, etc.
โ โโโ core/ # Business logic
โ โ โโโ engine.py # Audit orchestration
โ โ โโโ control.py # YAML control loader
โ โ โโโ evaluator.py # AST-sandboxed code evaluation
โ โ โโโ scoring.py # ROI calculations
โ โโโ integrations/ # External services
โ โ โโโ graph/ # MS Graph API client
โ โ โโโ osint/ # DNS graders + trusted OSINT executors
โ โโโ models/ # Pydantic data models
โ โโโ output/ # Report generators
โ โโโ services/ # Scheduler + webhook notifications
โ โโโ tui/ # Textual terminal UI
โ โโโ utils/ # Configuration, logging, cache, history
โโโ controls/ # YAML control definitions
โ โโโ iam/ # Identity & Access (18 controls)
โ โโโ atp/ # Advanced Threat Protection (12)
โ โโโ dgv/ # Data Governance (10)
โ โโโ dev/ # Device Management (8)
โ โโโ app/ # Application Security (6)
โ โโโ mon/ # Monitoring & Response (10)
โ โโโ ops/ # Security Operations (6)
โ โโโ biz/ # Business Optimization (12)
โ โโโ osint/ # Open Source Intelligence (1)
โโโ tests/ # Test suite
โ โโโ unit/ # Unit tests
โ โโโ integration/ # Integration tests (future)
โโโ docs/ # Documentation
โโโ reports/ # Audit output directory
โโโ templates/ # Report templates
| Component | Technology | Purpose |
|---|---|---|
| CLI Framework | Typer + Rich | Scriptable command-line interface |
| Terminal UI | Textual | Interactive dashboard, audit runner, findings triage |
| API Client | msgraph-sdk | Microsoft Graph API integration |
| Authentication | azure-identity | OAuth 2.0 / Client credentials |
| Data Models | Pydantic v2 | Type-safe data validation |
| Configuration | pydantic-settings | Environment-aware config |
| Templating | Jinja2 | HTML report generation |
| Testing | pytest + pytest-asyncio | Async-friendly test suite |
| Code Quality | ruff + mypy | Linting and type checking |
| Logging | structlog | Structured logging |
| Caching | diskcache | API response caching |
graph TD
A[CLI Command] --> B[Load Configuration]
B --> C[Initialize Graph Client]
C --> D[Authenticate with Azure AD]
D --> E[Load Control Definitions]
E --> F{For Each Control}
F --> G[Fetch Graph API Data]
G --> H[Execute Evaluation Logic]
H --> I[Calculate Complexity]
I --> J[Generate Finding]
J --> F
F --> K[Calculate ROI Scores]
K --> L[Prioritize Findings]
L --> M[Generate Reports]
M --> N[Export JSON/HTML]
| Control ID | Name | Severity | Complexity |
|---|---|---|---|
| IAM-001 | MFA for All Users | Critical | โญโญ 2/5 |
| IAM-002 | Conditional Access Policies | Critical | โญโญโญ 3/5 |
| IAM-003 | Password Protection Policies | High | โญโญ 2/5 |
| IAM-005 | Privileged Identity Management | High | โญโญโญโญ 4/5 |
| IAM-006 | Guest User Access Controls | Medium | โญโญ 2/5 |
| IAM-007 | Sign-in Risk Policies | High | โญโญโญ 3/5 |
| IAM-008 | User Risk Policies | High | โญโญโญ 3/5 |
| IAM-009 | Break-Glass Accounts | High | โญโญ 2/5 |
| IAM-010 | Session Controls | Medium | โญโญโญ 3/5 |
| IAM-011 | Self-Service Password Reset | Medium | โญ 1/5 |
| IAM-012 | Smart Lockout | Medium | โญ 1/5 |
| IAM-013 | Directory Synchronization | Medium | โญโญโญ 3/5 |
| IAM-014 | Legacy Authentication Blocking | High | โญโญ 2/5 |
| IAM-015 | Named Locations Configuration | Low | โญโญ 2/5 |
| IAM-016 | Terms of Use Policies | Low | โญโญ 2/5 |
| Control ID | Name | Severity | Complexity |
|---|---|---|---|
| ATP-001 | Safe Attachments for Exchange | Critical | โญโญ 2/5 |
| ATP-002 | Safe Links for Exchange | High | โญโญ 2/5 |
| ATP-003 | Advanced Anti-phishing | Critical | โญโญโญ 3/5 |
| ATP-004 | Anti-spam Policies | High | โญโญ 2/5 |
| ATP-005 | Anti-malware Policies | High | โญโญ 2/5 |
| ATP-006 | SPF Configuration | High | โญโญ 2/5 |
| ATP-007 | DKIM Signing | High | โญโญ 2/5 |
| ATP-008 | DMARC Policy | High | โญโญโญ 3/5 |
| ATP-009 | Safe Attachments for SharePoint/OneDrive | Medium | โญ 1/5 |
| ATP-010 | Quarantine Notifications | Low | โญ 1/5 |
| ATP-011 | Connection Filter (IP Lists) | Medium | โญโญ 2/5 |
| ATP-012 | Zero-hour Auto Purge | High | โญ 1/5 |
Includes DLP policies, sensitivity labels, data loss prevention, retention policies, and information protection.
Covers Intune compliance, BitLocker enforcement, MDM enrollment, and device configuration.
OAuth consent policies, risky app detection, service principal management.
Audit logging, alert policies, security monitoring, incident response, and threat detection.
Security contacts, privileged access workstations, emergency access accounts, security posture reviews, vulnerability management, and third-party risk assessment.
| Control ID | Name | Severity | Complexity |
|---|---|---|---|
| BIZ-001 | Unused License Detection | Medium | โญ 1/5 |
| BIZ-002 | Inactive User Cleanup | Medium | โญโญ 2/5 |
| BIZ-003 | Shared Mailbox Licensing | Low | โญ 1/5 |
| BIZ-004 | External Sharing Exposure | High | โญโญโญ 3/5 |
| BIZ-005 | Stale Guest Account Cleanup | Medium | โญโญ 2/5 |
| BIZ-006 | Email Forwarding Detection | High | โญโญ 2/5 |
| BIZ-007 | Unmanaged Mobile Devices | Medium | โญโญโญ 3/5 |
| BIZ-008 | Mailbox Delegation Audit | Medium | โญโญ 2/5 |
| BIZ-009 | Distribution List Sprawl | Low | โญโญ 2/5 |
| BIZ-010 | Teams External Access | Medium | โญโญ 2/5 |
| BIZ-011 | OneDrive Sync Restrictions | Medium | โญโญโญ 3/5 |
| BIZ-012 | Password Never Expires | Medium | โญโญ 2/5 |
Business Optimization Focus:
- Cost Savings (BIZ-001, 002, 003) - License waste detection and cleanup ($3K-$5K/year savings potential)
- Security Hardening (BIZ-004, 005, 006, 011) - Zero-cost security improvements for Business Standard tenants
- Compliance & Governance (BIZ-007, 008, 009, 010, 012) - Audit readiness and policy enforcement
| Control ID | Name | Severity | Complexity |
|---|---|---|---|
| OSINT-002 | Email Authentication DNS Health Check | High | โญโญโญ 3/5 |
OSINT Focus:
- External Validation - Real-time DNS lookups for SPF, DKIM, DMARC records (not simulated)
- Letter Grades - A-F grading system clients understand (e.g., "Your domain gets a C grade")
- Specific Findings - Identifies exact issues ("SPF uses ~all instead of -all")
- Microsoft 365 Optimized - Checks selector1 and selector2 DKIM selectors
- Zero Cost - Uses public DNS, no API keys required
- Replaces ATP-006 - ATP-006 simulated checks, OSINT-002 provides real external verification
Each control is defined in YAML with the following structure:
control_id: "IAM-001"
category: "IAM"
metadata:
name: "Multi-Factor Authentication for All Users"
description: "Ensures MFA is enforced for all user accounts"
severity: "critical"
framework_mappings:
cis: ["1.1.1", "1.1.2"]
nist: ["IA-2(1)", "IA-2(2)"]
iso27001: ["A.9.4.2"]
check:
type: "graph_api"
data_requirements:
- endpoint: "/policies/conditionalAccessPolicies"
permission: "Policy.Read.ConditionalAccess"
evaluation: |
def evaluate(data, tenant_context):
policies = data.get("policies", [])
mfa_policies = [p for p in policies if "mfa" in p.get("conditions", {}).get("applications", {}).get("includeApplications", [])]
return {
"status": "pass" if len(mfa_policies) > 0 else "fail",
"metrics": {
"total_policies": len(policies),
"mfa_policies": len(mfa_policies)
},
"evidence": {
"policy_names": [p["displayName"] for p in mfa_policies[:5]]
}
}
remediation:
summary: "Configure MFA enforcement through Conditional Access"
steps:
- "Navigate to Azure AD > Security > Conditional Access"
- "Create new policy: 'Require MFA for all users'"
- "Configure: All users, All cloud apps, Grant: Require MFA"
- "Enable policy and test with non-admin account"
documentation:
- title: "Microsoft Documentation"
url: "https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-enable-azure-mfa"
complexity:
base_score: 2
factors:
requires_licenses: ["Microsoft Entra ID P1"]
user_impact: "medium"
rollback_difficulty: "low"# Launch the TUI โ browse controls, run audits, triage findings
uv run sentinel tui# Full audit of all categories
uv run sentinel audit run --tenant contoso
# Quick scan (critical/high severity only)
uv run sentinel audit run --tenant contoso --quick
# Skip external OSINT checks (DNS lookups)
uv run sentinel audit run --tenant contoso --skip-osint
# Specific categories
uv run sentinel audit run --tenant contoso --categories IAM,ATP
# List available controls (filter by category/severity)
uv run sentinel audit list-controls --category IAM
# Show a specific control definition
uv run sentinel audit show IAM-001
# Convert an existing JSON report to HTML
uv run sentinel audit to-html reports/contoso_audit.json# Export format: json, html, or both
uv run sentinel audit run --tenant contoso --format both
# Verbose / JSON structured logs
uv run sentinel audit run --tenant contoso --verbose --log-json
# OSINT checks against a domain (no tenant needed)
uv run sentinel osint domain contoso.com
# Audit history and trends
uv run sentinel history list contoso
uv run sentinel history trend contoso
# Scheduled audits + webhook notifications
uv run sentinel schedule add nightly --tenant contoso --cron "0 2 * * *"
uv run sentinel schedule daemon
uv run sentinel webhook add slack --url https://hooks.example.com/sentinel --event audit.completed# Initialize new configuration
uv run sentinel config init
# Show current configuration (secrets redacted)
uv run sentinel config show
# Show a specific tenant's configuration
uv run sentinel config tenant contoso
# Test tenant connectivity/authentication
uv run sentinel config test contosoThe agent looks for configuration in the following order:
~/.m365sheppard/config.yaml(user-specific)./config.yaml(project directory)- Environment variables (override file settings)
version: "1"
settings:
output_dir: "./reports"
controls_dir: "./controls"
log_level: "INFO"
cache_enabled: true
cache_ttl: 3600 # seconds
tenants:
- name: "contoso"
tenant_id: "12345678-1234-1234-1234-123456789abc"
display_name: "Contoso Ltd"
authentication:
method: "client_credentials"
client_id: "your-app-client-id"
client_secret_env: "CONTOSO_CLIENT_SECRET"
tenant_id: "12345678-1234-1234-1234-123456789abc"
# Optional: Override default settings
output_dir: "./reports/contoso"
- name: "fabrikam"
tenant_id: "87654321-4321-4321-4321-cba987654321"
display_name: "Fabrikam Inc"
authentication:
method: "certificate"
client_id: "your-app-client-id"
certificate_path: "/path/to/cert.pem"
tenant_id: "87654321-4321-4321-4321-cba987654321"Create a .env file in the project root:
# Tenant authentication secrets
CONTOSO_CLIENT_SECRET=your-secret-here
FABRIKAM_CLIENT_SECRET=another-secret
# API settings (optional)
GRAPH_API_VERSION=v1.0
REQUEST_TIMEOUT=30
RATE_LIMIT_PER_MINUTE=100
# Output configuration (optional)
DEFAULT_OUTPUT_FORMAT=json
LOG_LEVEL=INFONavigate to Azure Portal โ Azure Active Directory โ App registrations โ Your App โ API permissions
Microsoft Graph (Application permissions):
SecurityEvents.Read.All- Read security events and reportsOrganization.Read.All- Read organization informationPolicy.Read.ConditionalAccess- Read conditional access policiesUser.Read.All- Read all users' full profilesDirectory.Read.All- Read directory dataAuditLog.Read.All- Read audit log data (optional but recommended)SecurityActions.Read.All- Read security actions (optional)
After adding permissions, click "Grant admin consent for [Your Organization]"
- Navigate to "Certificates & secrets"
- Click "New client secret"
- Add description: "M365 Sheppard Agent"
- Set expiration (recommended: 12 months)
- Copy the secret value immediately (store in
.envfile)
# Install development dependencies
uv sync --extra dev
# Run all tests
uv run pytest
# Run with verbose output
uv run pytest -v
# Run specific test file
uv run pytest tests/unit/test_scoring.py
# Run with coverage report
uv run pytest --cov=src/m365_sheppard --cov-report=html
# Run only unit tests
uv run pytest tests/unit/
# Run integration tests (requires Graph API credentials)
uv run pytest tests/integration/ -m integration337 tests cover the security-critical paths end to end: the AST sandbox (every documented bypass vector), the audit engine (dispatch, scoring, OSINT executors), configuration models, and the TUI (headless pilot tests for boot, browsing, filtering, findings triage, and audit wiring). Coverage is deliberately concentrated where correctness matters most โ the large presentation layers (TUI rendering, Graph client plumbing) have targeted smoke tests rather than line-by-line coverage.
# Lint code with ruff
uv run ruff check src/ tests/
# Format code with ruff
uv run ruff format src/ tests/
# Type checking with mypy
uv run mypy src/
# All quality checks
uv run ruff check src/ && uv run mypy src/ && uv run pytest- โ Project structure and architecture
- โ Pydantic data models
- โ CLI framework with Typer
- โ Graph API authentication
- โ YAML control definition schema
- โ Basic unit tests
- โ Safe control evaluation engine
- โ Configuration file management
- โ JSON/HTML report generation
- โ Real Graph API data collection
- โ 84 control definitions (all categories including OSINT)
- โ ROI-based prioritization
- โ Multi-tenant support
- โ Comprehensive testing
- โ Response caching with diskcache
- โ Structured logging with structlog
- โ OSINT integration (HIBP, DNS health checks)
- โ Trusted executor path for network-based controls
- โ Historical trending and analytics
- โ Scheduled auditing (cron-like daemon)
- โ Webhook notifications
- โ Interactive terminal UI (dashboard, controls browser, audit runner, findings triage)
- ๐ฎ REST API for external integrations
- ๐ฎ Ticket system integration (ServiceNow, Jira)
- ๐ฎ Automated remediation workflows
- ๐ฎ Compliance report templates (SOC 2, ISO 27001)
- ๐ฎ AI-powered remediation suggestions
- ๐ฎ Benchmark data (industry averages)
- ๐ฎ TUI: multi-tenant portfolio view and diff between audit runs
We welcome contributions from the community!
-
Fork the repository
git clone https://github.com/your-username/m365-sheppard.git cd m365-sheppard -
Setup development environment
uv sync --extra dev
-
Create feature branch
git checkout -b feature/your-feature-name
-
Make changes and test
# Run tests uv run pytest # Check code quality uv run ruff check src/ uv run mypy src/
-
Submit pull request
- Write clear commit messages
- Update documentation as needed
- Ensure all tests pass
- Add tests for new functionality
- Follow PEP 8 style guidelines
- Use type hints for all function signatures
- Write async-first code (use
async/awaitfor I/O operations) - Add docstrings to public functions and classes
- Maintain 80%+ test coverage for new code
- Update CLAUDE.md with new patterns or conventions
-
Create YAML file in appropriate category directory:
controls/iam/IAM-017.yaml
-
Follow the control schema (see existing controls for examples)
-
Test loading:
uv run sentinel audit show-control IAM-017
-
Submit PR with control definition and test cases
This project is licensed under the MIT License - see the LICENSE file for details.
- Microsoft Graph Team - For comprehensive security APIs
- CIS, NIST, ISO - For security control frameworks
- CoreView - For MSP best practices guidance
- Security Community - For feedback and contributions
| Channel | Use For | Response Time |
|---|---|---|
| GitHub Issues | Bug reports, Feature requests | 1-2 business days |
| GitHub Discussions | General questions, Ideas | 2-3 business days |
| Documentation | How-to guides, API reference | Self-service |
See docs/TROUBLESHOOTING.md for common issues and solutions.
Report security vulnerabilities to: security@m365sentinel.dev
Made with ๐ for better Microsoft 365 security
Current Version: 0.1.0 | Last Updated: July 2026 | Status: Phase 3 Complete