Skip to content

Repository files navigation

Entra ID Secrets Notification System

A Docker-based service that monitors Microsoft Entra ID (Azure AD) application secrets and certificates for expiration and sends notifications through multiple channels.

Built using Domain-Driven Design (DDD) and Hexagonal Architecture (Ports and Adapters) patterns, following Python 2025 best practices.

Features

  • Automatic Monitoring: Scans all app registrations and service principals in your Entra ID tenant
  • Service Principal Support: Monitors both App Registration and Service Principal credentials with separate report sections
  • Multiple Notification Channels: Email (SMTP), Microsoft Graph Email, Microsoft Teams, Slack, Generic Webhook
  • Configurable Thresholds: Critical, Warning, and Info levels
  • Flexible Scheduling: Run once or on a cron schedule
  • REST API: Optional API for health checks, reports, and on-demand checks (no credential details exposed)
  • Clean Architecture: DDD with hexagonal architecture for maintainability
  • Type Safety: Full type hints with Python 3.12+ features
  • Async/Await: Non-blocking I/O for better performance
  • Docker-Ready: Production-ready containerization

Architecture

This project follows Hexagonal Architecture (Ports and Adapters) with Domain-Driven Design principles:

src/
├── domain/                    # Core business logic (no external dependencies)
│   ├── entities/              # Domain entities with identity
│   │   ├── credential.py      # Credential entity
│   │   ├── application.py     # Application entity
│   │   └── expiration_report.py  # Aggregate root
│   ├── value_objects/         # Immutable value objects
│   │   ├── credential_source.py  # App Registration vs Service Principal
│   │   ├── credential_type.py
│   │   ├── expiration_status.py
│   │   ├── notification_level.py
│   │   └── thresholds.py
│   ├── services/              # Domain services
│   │   └── expiration_analyzer.py
│   └── exceptions.py          # Domain exceptions
│
├── application/               # Application layer (use cases)
│   ├── ports/                 # Interfaces (driven/secondary ports)
│   │   ├── credential_repository.py  # Port for data retrieval
│   │   └── notification_sender.py    # Port for notifications
│   ├── use_cases/             # Application services
│   │   └── check_expiring_credentials.py
│   └── exceptions.py          # Application exceptions
│
├── infrastructure/            # Infrastructure layer (adapters)
│   ├── adapters/
│   │   ├── api/               # REST API adapter (FastAPI)
│   │   │   ├── app.py
│   │   │   └── models.py
│   │   ├── entra_id/          # Entra ID adapter (implements CredentialRepository)
│   │   │   ├── graph_client.py
│   │   │   └── repository.py
│   │   └── notifications/     # Notification adapters (implement NotificationSender)
│   │       ├── email.py       # SMTP email
│   │       ├── graph_email.py # Microsoft Graph API email
│   │       ├── teams.py
│   │       ├── slack.py
│   │       └── webhook.py
│   └── config/                # Configuration loading
│       └── settings.py
│
└── main.py                    # Composition root (dependency injection)

Key Concepts

  • Domain Layer: Pure business logic with no external dependencies. Contains entities, value objects, and domain services.
  • Application Layer: Orchestrates use cases using domain objects. Defines ports (interfaces) for external systems.
  • Infrastructure Layer: Implements adapters that fulfill the ports. Contains all external system integrations.
  • Composition Root: Wires everything together using dependency injection.

Prerequisites

  1. Microsoft Entra ID App Registration with:

    • API Permission: Microsoft Graph > Application.Read.All (Application)
    • API Permission: Microsoft Graph > Mail.Send (Application) - only if using Graph Email
    • Admin consent granted
    • Client secret created
  2. Python 3.12+ (for local development)

  3. Docker and Docker Compose (for containerized deployment)

Quick Start

1. Clone and Configure

git clone <repository-url>
cd entra-id-secrets-notification
cp .env.example .env
# Edit .env with your Azure credentials and notification settings

2. Run with Docker Compose

# Build and run
docker compose up -d

# View logs
docker compose logs -f

# Stop
docker compose down

3. Run Once (Manual Check)

# Set RUN_MODE=once in .env, then:
docker compose up

Configuration

Required: Azure/Entra ID

Variable Description
AZURE_TENANT_ID Your Entra ID tenant ID
AZURE_CLIENT_ID App registration client ID
AZURE_CLIENT_SECRET App registration client secret

Thresholds

Variable Default Description
CRITICAL_THRESHOLD_DAYS 7 Days for critical alerts
WARNING_THRESHOLD_DAYS 30 Days for warning alerts
INFO_THRESHOLD_DAYS 90 Days for info alerts

Monitoring Scope

Variable Default Description
MONITOR_SERVICE_PRINCIPALS true Also monitor Service Principal credentials

Note: Service Principals are the tenant-local instances of applications. They often have their own secrets/certificates that are different from the App Registration. Both are monitored by default and shown in separate sections in notifications.

Schedule

Variable Default Description
RUN_MODE scheduled once or scheduled
CRON_SCHEDULE 0 8 * * * Cron expression
LOG_LEVEL INFO DEBUG, INFO, WARNING, ERROR
DRY_RUN false Test mode (no notifications)

Notification Channels

Email (SMTP)

SMTP_ENABLED=true
SMTP_SERVER=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=user
SMTP_PASSWORD=pass
SMTP_FROM=noreply@example.com
SMTP_TO=admin@example.com
SMTP_USE_TLS=true

Microsoft Teams

TEAMS_ENABLED=true
TEAMS_WEBHOOK_URL=https://outlook.office.com/webhook/...

Slack

SLACK_ENABLED=true
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...

Generic Webhook

WEBHOOK_ENABLED=true
WEBHOOK_URL=https://your-endpoint.com/notify

Microsoft Graph Email

Send email via Microsoft Graph API. Useful when SMTP is not available or you want to use Microsoft 365 mailboxes.

Note: Requires Mail.Send application permission with admin consent. If Graph-specific credentials are not set, it uses the main Azure credentials.

GRAPH_EMAIL_ENABLED=true
GRAPH_EMAIL_FROM=notifications@yourdomain.com
GRAPH_EMAIL_TO=admin@example.com,security@example.com
# Optional: Use different credentials than the main app
GRAPH_EMAIL_TENANT_ID=
GRAPH_EMAIL_CLIENT_ID=
GRAPH_EMAIL_CLIENT_SECRET=
# Save sent emails to Sent Items folder
GRAPH_EMAIL_SAVE_TO_SENT=false

REST API (Optional)

Enable the REST API for health checks, summary reports, and on-demand credential checks.

Security Note: The API does NOT expose credential details - only statistics and summaries.

API_ENABLED=true
API_HOST=0.0.0.0
API_PORT=8080

API Endpoints

Method Endpoint Description
GET /health Health check - returns service status
GET /api/v1/report Get latest report summary (no credential details)
POST /api/v1/check Trigger on-demand credential check

Example: Health Check

curl http://localhost:8080/health
{
  "status": "healthy",
  "version": "1.1.0",
  "timestamp": "2025-01-15T08:00:00Z"
}

Example: Get Report

curl http://localhost:8080/api/v1/report
{
  "generated_at": "2025-01-15T08:00:00Z",
  "notification_level": "warning",
  "summary": "5 credentials requiring attention: 1 expired, 2 critical, 2 warning",
  "statistics": {
    "total_applications": 3,
    "total_credentials": 5,
    "expired_count": 1,
    "critical_count": 2,
    "warning_count": 2,
    "healthy_count": 0
  },
  "thresholds": {
    "critical_days": 7,
    "warning_days": 30,
    "info_days": 90
  },
  "requires_notification": true
}

Example: Trigger Check

curl -X POST http://localhost:8080/api/v1/check

OpenAPI Documentation

When API is enabled, interactive documentation is available at:

  • Swagger UI: http://localhost:8080/docs
  • ReDoc: http://localhost:8080/redoc

Webhook JSON Payload

For generic webhook integrations:

{
  "event_type": "entra_id_secrets_alert",
  "timestamp": "2025-01-15T08:00:00+00:00",
  "level": "critical",
  "summary": "5 credentials requiring attention: 1 expired, 2 critical, 2 warning",
  "statistics": {
    "total_applications_affected": 3,
    "total_credentials": 5,
    "expired_count": 1,
    "critical_count": 2,
    "warning_count": 2,
    "healthy_count": 0
  },
  "credentials": [
    {
      "application_id": "app-guid",
      "application_name": "My Application",
      "credential_id": "credential-guid",
      "credential_type": "password",
      "display_name": "API Key",
      "expiry_date": "2025-01-20T00:00:00+00:00",
      "days_until_expiry": 5,
      "is_expired": false,
      "status": "critical"
    }
  ]
}

Setting Up Entra ID

  1. Go to Azure Portal > Microsoft Entra ID > App registrations

  2. Click New registration:

    • Name: Entra ID Secrets Monitor
    • Account types: Single tenant
    • Click Register
  3. Note the Application (client) ID and Directory (tenant) ID

  4. Certificates & secrets > Client secrets > New client secret

  5. API permissions > Add a permission:

    • Microsoft Graph > Application permissions
    • Add Application.Read.All (required for monitoring)
    • Add Mail.Send (only if using Microsoft Graph Email notifications)
    • Click Grant admin consent

Troubleshooting

Authentication Errors

  • Verify Azure credentials
  • Check client secret hasn't expired
  • Confirm admin consent was granted

No Notifications

  • Enable at least one notification channel
  • Check DRY_RUN=false
  • Verify webhook URLs

Permission Errors

  • Ensure Application.Read.All permission
  • Verify admin consent

View Logs

docker compose logs -f

License

MIT License - see LICENSE file.

About

Monitor Entra ID (Azure AD) application secrets and certificates for expiration. Sends alerts via Email, Teams, Slack, or Webhook before credentials expire.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages