This will monitor processes for Entra synchronisation from a point of view of when those processes are started and are stopped this includes "miisclient" and "adconnect" the first processes is the GUI management and the seconds process is to configure the application (which when run will stop synchronisations from occurring)
We need to monitor who has the process open and for how long, this will be done with a start and end time which will be logged when the process is started and quit, this will give you insights into the time the process was open.
Script : EntraProcessMonitor.ps1
# Create output file path in same directory as script
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$statsFile = Join-Path $scriptPath "process_monitoring.txt"
function Write-UsageLog {
param(
[string]$processName,
[string]$startTime,
[string]$endTime,
[string]$duration,
[string]$username
)
$logEntry = @"
==================
Usage Record: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Process: $processName
Start Time: $startTime
End Time: $endTime
Duration: $duration
Username: $username
==================
"@
Add-Content -Path $statsFile -Value $logEntry
}
Write-Host "Starting AD Connect process monitor..."
Write-Host "Logging to: $statsFile"
# Track both processes separately
$processInfo = @{
"miisclient" = @{
StartTime = $null
Username = $null
}
"AzureADConnect" = @{
StartTime = $null
Username = $null
}
}
while ($true) {
# Monitor both processes
$miisclientProcess = Get-Process miisclient -ErrorAction SilentlyContinue
$setupProcess = Get-Process AzureADConnect -ErrorAction SilentlyContinue
# Process monitoring function
foreach ($procType in @("miisclient", "AzureADConnect")) {
$currentProcess = if ($procType -eq "miisclient") { $miisclientProcess } else { $setupProcess } if ($currentProcess) {
# Process is running
if ($null -eq $processInfo[$procType].StartTime) {
# New process detected
$processInfo[$procType].StartTime = $currentProcess.StartTime
$processInfo[$procType].Username = (Get-WmiObject -Class Win32_Process -Filter "ProcessId = $($currentProcess.Id)").GetOwner().User
Write-Host "$procType process started at $($processInfo[$procType].StartTime) by $($processInfo[$procType].Username)"
}
}
else {
# Process not running
if ($null -ne $processInfo[$procType].StartTime) {
# Process has terminated
$endTime = Get-Date
$duration = $endTime - $processInfo[$procType].StartTime
Write-UsageLog `
-processName $procType `
-startTime $processInfo[$procType].StartTime `
-endTime $endTime `
-duration $duration `
-username $processInfo[$procType].Username
# Reset tracking for this process
$processInfo[$procType].StartTime = $null
$processInfo[$procType].Username = $null
}
}
}
# Check every 10 seconds
Start-Sleep -Seconds 10
}
When you run this script it will run continually so you may want to add it as a scheduled task with the trigger "on system startup"
The output will look like this which tells us that "miisclient" has been run by admin.user for 11 seconds with the date and timings:
==================
Usage Record: 2024-12-19 10:10:20
Process: miisclient
Start Time: 12/19/2024 10:10:09
End Time: 12/19/2024 10:10:20
Duration: 00:00:11.1743530
Username: admin.user
==================
The is the raw output, but what if we want an email based on this data? well, as it happens let look at that now, the result will look like this:
Script : Entra-Mailer.ps1# Email Configuration
$smtpServer = "smtpbear.local"
$fromAddress = "Process.Monitoring@croucher.cloud"
$toAddress = "lee@croucher.cloud"
$subject = "Process Monitoring Report - $(Get-Date -Format 'MMMM dd, yyyy')"
# Generate HTML Content
$htmlContent = @"
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
color: #333;
line-height: 1.6;
margin: 20px;
}
.header {
margin-bottom: 20px;
}
.process-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.process-table th {
background-color: #f5f5f5;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
.process-table td {
padding: 10px;
border: 1px solid #ddd;
}
.warning-text {
color: #ff4444;
font-size: 0.9em;
margin-top: 20px;
}
.amber-row {
background-color: rgba(255, 165, 0, 0.15);
}
.red-row {
background-color: rgba(255, 0, 0, 0.15);
}
</style>
</head>
<body>
<div class="header">
<h2>Process Monitoring Report</h2>
<p>Date: $(Get-Date -Format 'MMMM dd, yyyy')</p>
</div>
<table class="process-table">
<thead>
<tr>
<th>Process</th>
<th>Duration</th>
<th>Username</th>
</tr>
</thead>
<tbody>
"@
# Read the monitoring file and parse records
$content = Get-Content "process_monitoring.txt"
$currentRecord = @{}
$records = @()
foreach ($line in $content) {
if ($line -match '^=+$') {
if ($currentRecord.Count -gt 0) {
$records += [PSCustomObject]$currentRecord
$currentRecord = @{}
}
continue
}
if ($line -match '^Process: (.+)$') {
$currentRecord.Process = $matches[1].Trim()
}
elseif ($line -match '^Duration: (.+)$') {
$currentRecord.Duration = $matches[1].Trim()
}
elseif ($line -match '^Username: (.+)$') {
$currentRecord.Username = $matches[1].Trim()
}
}
# Add the last record if exists
if ($currentRecord.Count -gt 0) {
$records += [PSCustomObject]$currentRecord
}
# Generate table rows
foreach ($record in $records) {
$rowClass = if ($record.Process -eq 'miisclient') { 'amber-row' } else { 'red-row' }
$duration = [TimeSpan]::Parse($record.Duration).TotalSeconds.ToString("F2")
$htmlContent += @"
<tr class="$rowClass">
<td>$($record.Process)</td>
<td>$duration seconds</td>
<td>$($record.Username)</td>
</tr>
"@
}
$htmlContent += @"
</tbody>
</table>
<p class="warning-text">
<strong>Important Note:</strong> The AzureADConnect process can interrupt synchronization if left running. Please ensure to close it after configuration changes are complete.
</p>
</body>
</html>
"@
# Configure email parameters
$mailParams = @{
SmtpServer = $smtpServer
From = $fromAddress
To = $toAddress
Subject = $subject
Body = $htmlContent
BodyAsHtml = $true
}
# Send the email
try {
Send-MailMessage @mailParams
Write-Host "Email sent successfully!"
} catch {
Write-Error "Failed to send email: $_"
}