Invoke-EntraDirectorySyncCheck.ps1 is a PowerShell 7+ script designed to analyze Azure Entra ID directory synchronization status, enumerate sync errors, identify sync conflicts, and check sync scope and filters. This tool is part of the EvilMist toolkit and helps security teams and IT administrators assess the health and configuration of directory synchronization in their Azure AD tenant.
Directory synchronization is critical for hybrid identity environments. This script helps:
- IT Administrators: Monitor sync health and identify sync issues
- Security Teams: Assess sync configuration and detect sync-related security gaps
- Compliance Teams: Generate reports for sync status and error tracking
- Penetration Testers: Identify sync misconfigurations and potential attack vectors
- ✅ PowerShell 7+ Compatible: Modern PowerShell for cross-platform support
- ✅ Multiple Authentication Methods: Supports Azure CLI, Azure PowerShell, and interactive auth
- ✅ Comprehensive Sync Analysis: Identifies synced vs cloud-only users
- ✅ Sync Error Detection: Enumerates all provisioning and sync errors
- ✅ Stale Sync Detection: Identifies users with stale synchronization (>7 days)
- ✅ Sync Conflict Identification: Detects duplicate attributes and conflicts
- ✅ Sync Scope Analysis: Checks sync configuration and scope
- ✅ Risk Assessment: Categorizes users by risk level (CRITICAL/HIGH/MEDIUM/LOW)
- ✅ Activity Analytics: Sync statistics, error breakdowns, domain analysis
- ✅ Stealth Mode: Configurable delays and jitter to avoid detection
- ✅ Export Options: CSV and JSON export formats
- ✅ Matrix View: Table format with analytics for quick visual scanning
- ✅ Filtering Options: Show only sync errors, stale sync, or include disabled accounts
The script analyzes directory synchronization across multiple dimensions:
-
On-Premises Synced Users
- Users synchronized from on-premises Active Directory
- Identified by
onPremisesSyncEnabled,onPremisesImmutableId, oronPremisesSecurityIdentifier - Includes domain, SAM account name, and distinguished name information
-
Cloud-Only Users
- Users created directly in Azure AD
- No on-premises synchronization
- Typically service accounts, guest users, or cloud-native accounts
- Last Sync Timestamp: Shows when each user was last synchronized
- Days Since Last Sync: Calculates sync staleness
- Sync Errors: Enumerates provisioning errors and conflicts
- Error Categories: Groups errors by type (PropertyConflict, DuplicateAttribute, etc.)
-
PowerShell 7+
- Download: https://aka.ms/powershell-release?tag=stable
- The script will check and warn if older version is detected
-
Microsoft Graph PowerShell SDK
Install-Module Microsoft.Graph -Scope CurrentUser
Or install individual modules:
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser Install-Module Microsoft.Graph.Users -Scope CurrentUser Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser
The script requires the following Microsoft Graph API permissions:
-
Primary Scopes (preferred):
Directory.Read.All- Read directory data and sync configurationUser.Read.All- Read all user profiles and sync propertiesAuditLog.Read.All- Read audit logs (optional, for sync history)
-
Fallback Scopes (if full access unavailable):
Directory.Read.All- Read directory dataUser.ReadBasic.All- Read basic user info
Note: Full sync error details require User.Read.All permission. With reduced permissions, basic sync status will still be available.
# Simple scan of all users' sync status
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1
# During the scan, you'll see sync status for all users:
# [+] Retrieved 1250 total users
# [*] Analyzing sync status for each user...# Export to CSV
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -ExportPath "sync-status.csv"
# Export to JSON
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -ExportPath "sync-results.json"# Scan all users including disabled accounts
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -IncludeDisabledUsers -ExportPath "all-sync-status.csv"# Filter to show only users with sync errors
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlySyncErrors
# Matrix view with error filter
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlySyncErrors -Matrix# Filter to show only users with stale sync (>7 days)
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlyStaleSync
# Export stale sync users
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlyStaleSync -ExportPath "stale-sync.csv"# Display results in compact matrix format
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -Matrix
# Matrix view with export
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -Matrix -ExportPath "results.csv"# Use Azure CLI cached credentials
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -UseAzCliToken
# Use Azure PowerShell cached credentials
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -UseAzPowerShellToken
# Specify tenant
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -TenantId "your-tenant-id"# Enable stealth mode with default settings (500ms delay + 300ms jitter)
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -EnableStealth
# Stealth mode with minimal output
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -EnableStealth -QuietStealth
# Custom delay and jitter
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -RequestDelay 1.5 -RequestJitter 0.5
# Maximum stealth with custom retry
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -EnableStealth -MaxRetries 5 -QuietStealth# Comprehensive audit: all users, all sync data, with export
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -IncludeDisabledUsers -Matrix -ExportPath "full-sync-audit.csv"
# Security focus: sync errors only
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlySyncErrors -Matrix -ExportPath "sync-errors.csv"
# Stale sync analysis
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlyStaleSync -Matrix -ExportPath "stale-sync-analysis.csv"
# Stealth reconnaissance with Azure CLI token
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -UseAzCliToken -EnableStealth -QuietStealth -ExportPath "recon.json"| Parameter | Type | Description | Default |
|---|---|---|---|
-ExportPath |
String | Path to export results (CSV or JSON based on extension) | None |
-TenantId |
String | Optional Tenant ID. Uses home tenant if not specified | None |
-UseAzCliToken |
Switch | Try to use Azure CLI cached token first | False |
-UseAzPowerShellToken |
Switch | Try to use Azure PowerShell cached token first | False |
-IncludeDisabledUsers |
Switch | Include disabled user accounts in results | False |
-OnlySyncErrors |
Switch | Show only users with sync errors | False |
-OnlyStaleSync |
Switch | Show only users with stale sync (>7 days) | False |
-Matrix |
Switch | Display results in matrix/table format | False |
| Parameter | Type | Range | Description | Default |
|---|---|---|---|---|
-EnableStealth |
Switch | - | Enable stealth mode with default delays (500ms + 300ms jitter) | False |
-RequestDelay |
Double | 0-60 | Base delay in seconds between API requests | 0 |
-RequestJitter |
Double | 0-30 | Random jitter range in seconds (+/-) | 0 |
-MaxRetries |
Int | 1-10 | Maximum retries on throttling (429) responses | 3 |
-QuietStealth |
Switch | - | Suppress stealth-related status messages | False |
The script provides detailed information about each user's sync status:
[HIGH] john.doe@company.com
Display Name: John Doe
Sync Source: On-Premises AD
On-Premises Domain: COMPANY.LOCAL
SAM Account Name: jdoe
Distinguished Name: CN=John Doe,OU=Users,DC=company,DC=local
Account Status: Enabled
Last Sync: 2024-12-20 14:23:45 (3 days ago)
Sync Errors: 2 error(s)
Error Categories: PropertyConflict, DuplicateAttribute
Error Details:
- Category: PropertyConflict
Property: mail
Value: john.doe@company.com
Occurred: 2024-12-18T10:30:00Z
- Category: DuplicateAttribute
Property: userPrincipalName
Value: john.doe@company.com
Occurred: 2024-12-18T10:30:00Z
Risk Factors: 2 sync error(s)
Risk Source Status User Principal Name Display Name Domain Last Sync Errors Error Categories
---- ------ ------ ------------------- ------------ ------ ---------- ------ ---------------
HIGH On-Premises AD Enabled john.doe@company.com John Doe COMPANY.LOCAL 3d ago 2 PropertyConflict, DuplicateAttribute
LOW Cloud-Only Enabled jane.smith@company.com Jane Smith - - 0 -
MEDIUM On-Premises AD Enabled bob.wilson@company.com Bob Wilson COMPANY.LOCAL 15d ago 0 -
The script provides comprehensive statistics:
[SUMMARY]
Total users analyzed: 1250
Users in results: 1250
- Synced from On-Premises: 850
- Cloud-Only: 400
- Users with Sync Errors: 12
- Stale Sync (>7 days): 45
[RISK BREAKDOWN]
- CRITICAL risk: 0
- HIGH risk: 12
- MEDIUM risk: 45
- LOW risk: 1193
[USERS BY DOMAIN]
COMPANY.LOCAL: 850
SUBSIDIARY.LOCAL: 120
[ERROR CATEGORIES]
PropertyConflict: 8
DuplicateAttribute: 4
QuotaExceeded: 2
The script assigns risk levels based on sync health and errors:
| Risk Level | Criteria | Color | Recommendation |
|---|---|---|---|
| CRITICAL | Multiple sync errors or very stale sync (>30 days) | Red | IMMEDIATE ACTION REQUIRED: Resolve sync errors or investigate stale sync |
| HIGH | Users with sync errors | Red | URGENT: Fix sync errors to prevent authentication issues |
| MEDIUM | Stale sync (>7 days but ≤30 days) | Yellow | REVIEW: Investigate why sync is stale, may indicate sync service issues |
| LOW | Healthy sync or cloud-only users | Green | MONITOR: Normal sync status |
IF user has sync errors:
RISK = HIGH (Sync errors can cause authentication failures)
ELSE IF user is synced AND days since last sync > 30:
RISK = HIGH (Very stale sync indicates sync service failure)
ELSE IF user is synced AND days since last sync > 7:
RISK = MEDIUM (Stale sync may indicate issues)
ELSE:
RISK = LOW (Healthy sync or cloud-only user)
IMPORTANT: Directory synchronization status affects authentication and access:
| Sync Status | What It Means | Security Implications |
|---|---|---|
| Synced from On-Premises | User account synchronized from AD | Password changes happen on-premises, may bypass cloud MFA policies |
| Cloud-Only | User created directly in Azure AD | Full cloud control, subject to all cloud security policies |
| Sync Errors | Synchronization failed | User may not be able to authenticate, or may have inconsistent attributes |
| Stale Sync | No sync in 7+ days | May indicate sync service issues, password changes may not sync |
- On-Premises Synced Users: Password changes happen on-premises, may bypass cloud password policies
- Sync Errors: Can cause authentication failures or inconsistent user attributes
- Stale Sync: May indicate sync service issues or configuration problems
- Duplicate Attributes: Can cause authentication conflicts and security issues
- PropertyConflict: Attribute value conflicts between on-premises and cloud
- DuplicateAttribute: Duplicate attribute values (e.g., duplicate UPNs)
- QuotaExceeded: Sync quota limits exceeded
- InvalidAttributeValue: Invalid attribute value format or content
- ObjectNotFound: Referenced object not found in on-premises AD
- Monitor sync errors regularly - Fix errors promptly to prevent authentication issues
- Investigate stale sync - May indicate sync service problems or configuration issues
- Review sync scope - Ensure only necessary OUs are synchronized
- Use sync filters - Limit sync scope to reduce attack surface
- Monitor sync health - Set up alerts for sync errors and stale sync
Sync issues can lead to:
- Authentication Failures: Users unable to sign in due to sync errors
- Security Gaps: Stale sync may bypass security policy updates
- Data Inconsistency: Attribute conflicts can cause access issues
- Compliance Violations: Sync errors may prevent proper access controls
- Privilege Escalation: Duplicate attributes or sync conflicts can be exploited
-
Users with Sync Errors (HIGH Risk)
- May be unable to authenticate
- Attribute conflicts can cause access issues
- May indicate configuration problems
-
Stale Sync (MEDIUM-HIGH Risk)
- Password changes may not sync
- Security policy updates may not apply
- May indicate sync service failure
-
Duplicate Attributes (HIGH Risk)
- Can cause authentication conflicts
- May allow unauthorized access
- Indicates data quality issues
- Regular Monitoring: Run weekly to track sync health
- Error Resolution: Fix sync errors promptly to prevent authentication issues
- Stale Sync Investigation: Investigate users with stale sync (>7 days)
- Sync Scope Review: Regularly review sync scope and filters
- Documentation: Maintain records of sync configuration and changes
- Sync Health Assessment: Use as part of security audits
- Error Analysis: Analyze sync errors for security implications
- Compliance Reporting: Generate reports for compliance audits
- Attack Surface Analysis: Identify sync-related security gaps
- Monitoring: Set up alerts for sync errors and stale sync
- Reconnaissance: Identify sync configuration and scope
- Error Exploitation: Look for sync errors that may indicate misconfigurations
- Stale Sync Analysis: Identify users with stale sync for potential exploitation
- Scope Analysis: Understand sync scope to identify attack vectors
- Stealth Operations: Use
-EnableStealthto avoid detection
- Documentation: Export results regularly for audit trails
- Error Tracking: Monitor sync errors for compliance issues
- Health Reporting: Generate reports for sync health assessments
- Trend Analysis: Compare results over time to track improvements
- Remediation Tracking: Monitor sync error resolution progress
Includes all fields for analysis:
- DisplayName, UserPrincipalName, Email
- AccountEnabled, UserType
- SyncSource, OnPremisesSyncEnabled
- OnPremisesDomainName, OnPremisesSamAccountName, OnPremisesDistinguishedName
- OnPremisesLastSyncDateTime, DaysSinceLastSync
- OnPremisesImmutableId, OnPremisesSecurityIdentifier
- HasSyncErrors, ErrorCount, ErrorCategories
- ErrorDetailsJSON (JSON string of error details)
- RiskLevel, RiskFactors
Structured format for automation:
[
{
"DisplayName": "John Doe",
"UserPrincipalName": "john.doe@company.com",
"Email": "john.doe@company.com",
"SyncSource": "On-Premises AD",
"OnPremisesSyncEnabled": true,
"OnPremisesDomainName": "COMPANY.LOCAL",
"OnPremisesLastSyncDateTime": "2024-12-20T14:23:45Z",
"DaysSinceLastSync": 3,
"HasSyncErrors": true,
"ErrorCount": 2,
"ErrorCategories": "PropertyConflict, DuplicateAttribute",
"RiskLevel": "HIGH",
"RiskFactors": "2 sync error(s)"
}
]Cause: No users found or insufficient permissions.
Solution:
- Verify you have
Directory.Read.AllandUser.Read.Allpermissions - Check if tenant has any synced users
- Try with
-IncludeDisabledUsersto include all users
Cause: Insufficient Graph API permissions.
Solution:
# Disconnect and reconnect with proper scopes
Disconnect-MgGraph
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1
# Accept permission consent when promptedCause: Missing or outdated Microsoft.Graph modules.
Solution:
# Update all Graph modules
Update-Module Microsoft.Graph -Force
# Or reinstall
Uninstall-Module Microsoft.Graph -AllVersions
Install-Module Microsoft.Graph -Scope CurrentUserCause: Large number of users or throttling.
Solution:
# Use stealth mode to handle throttling
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -EnableStealth -MaxRetries 5
# Or filter results
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlySyncErrorsCause: Sync configuration may not be accessible via Graph API.
Solution:
- Full sync configuration requires Azure AD Connect PowerShell module
- Basic sync status is available via Graph API
- Check Azure AD Connect server for detailed configuration
# Check sync status for all users
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -Matrix -ExportPath "sync_health_$(Get-Date -Format 'yyyy-MM-dd').csv"Output: CSV file with all users, sync status, and health indicators.
# Find users with sync errors
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlySyncErrors -Matrix
# Review output, then remediateUse Case: Identify immediate sync issues for remediation.
# Find users with stale sync
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -OnlyStaleSync -ExportPath "stale-sync.csv"
# Investigate why sync is staleUse Case: Identify sync service issues or configuration problems.
# Full sync audit including disabled accounts
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -IncludeDisabledUsers -Matrix -ExportPath "full-sync-audit.csv"
# Compare with previous month's reportUse Case: Track sync health changes and error resolution over time.
# Scan specific tenant
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -TenantId "customer-tenant-id" -ExportPath "customer-sync.csv"
# Repeat for each tenantUse Case: MSP or consulting engagement across multiple tenants.
# Schedule weekly sync health checks
$scheduledScript = {
$date = Get-Date -Format "yyyy-MM-dd"
$path = "C:\SyncAudits\SyncHealth_$date.csv"
C:\Tools\Invoke-EntraDirectorySyncCheck.ps1 -Matrix -ExportPath $path
# Send alert if sync errors found
$results = Import-Csv $path
$syncErrors = $results | Where-Object { $_.HasSyncErrors -eq "True" }
if ($syncErrors.Count -gt 0) {
Send-MailMessage -To "it@company.com" `
-Subject "ALERT: $($syncErrors.Count) users with sync errors" `
-Body "Review attached report." `
-Attachments $path `
-SmtpServer "smtp.company.com"
}
}
# Create scheduled task (run as admin)
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At 6am
$action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-File C:\Scripts\WeeklySyncCheck.ps1"
Register-ScheduledTask -TaskName "Weekly Sync Health Check" -Trigger $trigger -Action $action# Export JSON for SIEM ingestion
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -ExportPath "siem_feed.json"
# Post-process for your SIEM format
$results = Get-Content "siem_feed.json" | ConvertFrom-Json
$siemEvents = $results | ForEach-Object {
@{
timestamp = (Get-Date).ToString("o")
event_type = "azure_sync_status"
severity = $_.RiskLevel
user = $_.UserPrincipalName
sync_source = $_.SyncSource
has_errors = $_.HasSyncErrors
error_count = $_.ErrorCount
days_since_sync = $_.DaysSinceLastSync
}
}
$siemEvents | ConvertTo-Json | Out-File "siem_formatted.json"# Run remotely on jump box or admin workstation
$session = New-PSSession -ComputerName "admin-server.company.com"
Invoke-Command -Session $session -ScriptBlock {
cd C:\Tools
.\scripts\powershell\Invoke-EntraDirectorySyncCheck.ps1 -Matrix -ExportPath "C:\Reports\sync.csv"
}
# Retrieve results
Copy-Item -FromSession $session -Path "C:\Reports\sync.csv" -Destination ".\local_copy.csv"
Remove-PSSession $session- Initial implementation
- Sync status detection (synced vs cloud-only)
- Sync error enumeration
- Stale sync detection (>7 days)
- Risk assessment framework
- Matrix view and export capabilities
- Stealth mode with configurable delays
- Multiple authentication methods
- Comprehensive sync analytics
This script is part of the EvilMist toolkit.
Copyright (C) 2025 Logisek
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See the LICENSE file for more details.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
Visit: https://github.com/Logisek/EvilMist
For questions, issues, or feature requests:
- GitHub Issues: https://github.com/Logisek/EvilMist/issues
- Email: info@logisek.com
- Website: https://logisek.com
- Invoke-EntraRecon.ps1: Comprehensive Azure AD reconnaissance
- Invoke-EntraMFACheck.ps1: MFA compliance audit
- Invoke-EntraGuestCheck.ps1: Guest account security analysis
- Invoke-EntraStaleAccountCheck.ps1: Stale account and account hygiene check