Microsoft Entra App Registration Credential Expiry Checker
Project description
Microsoft Entra App Registration Credential Expiry Checker
A Python package for monitoring and alerting on expiring secrets and certificates in Microsoft Entra ID (formerly Azure AD) App Registrations. This tool helps you stay ahead of credential expiry issues by automatically checking your App Registrations and sending email notifications when secrets/certificates are nearing expiration.
Features
- 🔍 Flexible Discovery: Check all App Registrations in your tenant or specify via Azure Table Storage
- 📧 Email Notifications: Send alerts via SendGrid when credentials are nearing expiration
- 🔐 Secure Authentication: Uses Azure CLI or Managed Identity authentication for secure access
- 📊 Detailed Reporting: Comprehensive logs and summary of findings
- 🚀 Easy Deployment: Works locally, with GitHub Actions, Azure DevOps, or any CI/CD platform
Installation
From PyPI (Recommended)
pip install entra-expiry-checker
Quick Start
1. Set up Authentication
First, ensure you have the Azure CLI installed in your environment and that you are authenticated with Azure:
az login
Note: For Azure hosted or CI/CD deployments, Azure Managed Identity can also be used (where supported by the CI/CD platform).
Required Permissions
The identity being used (Azure CLI logged in user or Managed Identity) must have the ability to read Applications and Users from the directory. The following Microsoft Graph API permissions can be applied to an Managed Identity to achieve this.
- Application.Read.All - Required to read App Registration details
- User.ReadBasic.All - Required to read user information for app owners
2. Configure Environment Variables
Set up your SendGrid API key and other required variables:
export SG_API_KEY="SG.your_sendgrid_api_key"
export FROM_EMAIL="noreply@yourdomain.com"
3. Run the Checker
entra-expiry-checker
Or run directly with Python:
python -m entra_expiry_checker.main
Configuration
Environment Variables
| Variable | Required | Description | Default |
|---|---|---|---|
SG_API_KEY |
Yes | SendGrid API key | - |
FROM_EMAIL |
Yes | Sender email address | - |
MODE |
No | Operation mode (tenant or storage) |
tenant |
DAYS_THRESHOLD |
No | Days before expiry to alert | 30 |
VERIFY_SSL |
No | Enable/disable SSL verification | true |
Tenant Mode Variables
| Variable | Required | Description |
|---|---|---|
DEFAULT_NOTIFICATION_EMAIL |
No | Default email for apps without owners |
Storage Mode Variables
| Variable | Required | Description |
|---|---|---|
STG_ACCT_NAME |
Yes | Azure Storage account name |
STG_ACCT_TABLE_NAME |
Yes | Table name in storage account |
Operation Modes
Tenant Mode
Checks all App Registrations in your Entra ID tenant.
How It Works
- Discovery: Reads all App Registrations from the authenticated Entra tenant
- Validation: Fetches App Registration details from Microsoft Graph API
- Checking: Examines secrets and certificates for each app
- Notification: Sends email alerts to the app owners (if set) + email configured in
DEFAULT_NOTIFICATION_EMAILenvironment variable (if set) - Reporting: Provides summary of processed applications
Storage Mode
Reads onboarded App Registrations from Azure Table Storage. This mode is useful when you want to check specific App Registrations rather than all apps in your tenant.
Storage Mode Prerequisites
- Azure Storage Account: You need an Azure Storage account with Table Storage enabled
- Table Structure: Create a table with the following schema:
- PartitionKey: Email address of the person or distribution list to notify
- RowKey: Object ID of the app registration
Table Schema Example
| PartitionKey | RowKey |
|---|---|
| admin@company.com | 12345678-1234-1234-1234-123456789012 |
| dev@company.com | 87654321-4321-4321-4321-210987654321 |
How It Works
- Discovery: Reads all entities from the specified Azure Table
- Validation: Fetches app registration details from Microsoft Graph API
- Checking: Examines secrets and certificates for each app
- Notification: Sends email alerts to the email addresses specified in PartitionKey
- Reporting: Provides summary of processed applications
Note: In Storage Mode, notifications are sent only to the email addresses specified in the Azure Table Storage (PartitionKey), not to the actual owners of the App Registrations. This allows you to control who receives notifications regardless of the app's ownership in Entra ID.
Benefits
- Targeted Monitoring: Only check specific App Registrations
- Flexible Notifications: Different people can be notified for different apps
- Audit Trail: Track which apps are being monitored
- Cost Effective: Avoid checking unnecessary applications
CI/CD Integration
GitHub Actions
Recommended Authentication: For GitHub Actions, it's recommended to use an Azure user-assigned managed identity with federated credentials. This provides secure, credential-free authentication without storing secrets. See Microsoft's guide for setup instructions.
name: Check App Registration Credential Expiry
on:
schedule:
# Run daily at 9 AM UTC
- cron: '0 9 * * *'
workflow_dispatch: # Allow manual triggering
env:
PYTHON_VERSION: '3.13'
permissions:
id-token: write
contents: read
jobs:
check-credentials:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install entra-expiry-checker
- name: Azure Login
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Check credential expiry
run: entra-expiry-checker
env:
# SendGrid configuration (always required)
SG_API_KEY: ${{ secrets.SG_API_KEY }}
FROM_EMAIL: ${{ vars.FROM_EMAIL }}
# Operation mode
MODE: ${{ vars.MODE || 'tenant' }}
# Days threshold
DAYS_THRESHOLD: ${{ vars.DAYS_THRESHOLD || '30' }}
# Tenant mode configuration (optional if MODE=tenant)
DEFAULT_NOTIFICATION_EMAIL: ${{ vars.DEFAULT_NOTIFICATION_EMAIL }}
# Storage mode configuration (only needed if MODE=storage)
STG_ACCT_NAME: ${{ vars.STG_ACCT_NAME }}
STG_ACCT_TABLE_NAME: ${{ vars.STG_ACCT_TABLE_NAME }}
# SSL verification (set to false to disable SSL certificate verification)
VERIFY_SSL: ${{ vars.VERIFY_SSL || 'true' }}
- name: Handle failure
if: failure()
run: |
echo "❌ Credential expiry check failed!"
echo "Check the logs above for details."
# Could add additional notification here (Slack, Teams, etc.)
Azure DevOps
Recommended Authentication: For Azure DevOps, it's recommended to use workload identity federation with a user-assigned managed identity. This provides secure, credential-free authentication without storing secrets. See Microsoft's guide for setup instructions.
trigger: none # No CI trigger - only scheduled runs
schedules:
- cron: "0 9 * * *" # Daily at 9 AM UTC
displayName: Check App Registration Credential Expiry
branches:
include:
- main # Modify as appropriate
always: true
pool:
vmImage: 'ubuntu-latest'
variables:
PYTHON_VERSION: '3.13'
stages:
- stage: CheckCredentials
displayName: 'Check App Registration Credential Expiry'
jobs:
- job: CheckCredentials
displayName: 'Check Credential Expiry'
steps:
- task: UsePythonVersion@0
displayName: 'Set up Python'
inputs:
versionSpec: '$(PYTHON_VERSION)'
addToPath: true
- task: AzureCLI@2
displayName: 'Login and Run Tooling'
inputs:
azureSubscription: '$(AZURE_SUBSCRIPTION)' # Service connection name
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
echo "Successfully authenticated with Azure"
az account show
# Install Python dependencies
python -m pip install --upgrade pip
pip install entra-expiry-checker
# Run the credential check script
entra-expiry-checker
env:
# SendGrid configuration (always required)
SG_API_KEY: $(SG_API_KEY)
FROM_EMAIL: $(FROM_EMAIL)
# Tenant mode configuration (only needed if MODE=tenant)
DEFAULT_NOTIFICATION_EMAIL: $(DEFAULT_NOTIFICATION_EMAIL)
# Storage mode configuration (only needed if MODE=storage)
STG_ACCT_NAME: $(STG_ACCT_NAME)
STG_ACCT_TABLE_NAME: $(STG_ACCT_TABLE_NAME)
- script: |
echo "❌ Credential expiry check failed!"
echo "Check the logs above for details."
# Could add additional notification here (Slack, Teams, etc.)
displayName: 'Handle failure'
condition: failed()
Contributing
- 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
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
Changelog
1.0.1 (2025-06-05)
- Documentation updates
1.0.0 (2025-06-05)
- Initial release
- Support for tenant and storage modes
- SendGrid email notifications
- Azure CLI + Managed Identity authentication
- Comprehensive configuration validation
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file entra_expiry_checker-1.0.2.tar.gz.
File metadata
- Download URL: entra_expiry_checker-1.0.2.tar.gz
- Upload date:
- Size: 23.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ba49bc9b7efb6567fb3ccf583d9ae64576bcba4f8100b27c152d9c923bf91e4
|
|
| MD5 |
67464b1959d797449d75a489dd99e6cf
|
|
| BLAKE2b-256 |
dc1fa1cd2085768a4d2ca4379a553d5ef3b072bbb0c0e4168612e30d5f7f49fc
|
File details
Details for the file entra_expiry_checker-1.0.2-py3-none-any.whl.
File metadata
- Download URL: entra_expiry_checker-1.0.2-py3-none-any.whl
- Upload date:
- Size: 22.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bbcd562579a29af2bbc7b935d7537be62fe41e8fa478bd1bb66531913c6054d
|
|
| MD5 |
72667f17a27ede85e3e5136f25fba586
|
|
| BLAKE2b-256 |
83629e89079534fe4c0505871ef1e8fe9680f885eea7930fd397e109e59610f7
|