Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.
🚀 Launch ToolkitWhen users forget their passwords, administrators often step in to reset them. While this is a routine operation, it’s also a high-impact action that can affect account security. Tracking who reset which user’s password and when is vital for both compliance and forensic investigations. The following script queries Entra ID Audit Logs for the “Reset user password” activity in the UserManagement category, exports the results to CSV, and emails the report to administrators or stakeholders.
# ===== Entra ID Audit: "Update user" (UserManagement) -> CSV -> Email =====
# Requires: Microsoft.Graph module
# Scopes: AuditLog.Read.All, Mail.Send
# -------- Email & Time Window ----------
$FromUser = "samadmin@7xh7fj.onmicrosoft.com" # Sender (must have mailbox)
$ToList = "dexter@7xh7fj.onmicrosoft.com" # Recipients (; or , separated)
$Subject = 'Entra ID Audit: "Password Resets by Admin" Activity Report'
$DaysBack = 7 # Lookback window (days)
$CsvOutDir = "$env:TEMP"
# -------- Build filter (UTC ISO format) ----------
$sinceIso = (Get-Date).ToUniversalTime().AddDays(-1 * $DaysBack).ToString("o")
$filter = "category eq 'UserManagement' and activityDisplayName eq 'Reset user password' and activityDateTime ge $sinceIso"
# -------- Query Audit Logs (Directory Audits) ----------
$auditEntries = Get-MgAuditLogDirectoryAudit -All -Filter $filter `
-Property "activityDateTime,activityDisplayName,category,correlationId,result,resultReason,loggedByService,initiatedBy,targetResources,additionalDetails"
# -------- Shape rows for CSV ----------
$rows = foreach ($e in $auditEntries) {
# Initiator (user or app)
$initiatorUpn = $null; $initiatorName = $null
try {
if ($e.InitiatedBy.User) {
$initiatorUpn = $e.InitiatedBy.User.UserPrincipalName
$initiatorName = $e.InitiatedBy.User.DisplayName
} elseif ($e.InitiatedBy.App) {
$initiatorUpn = $e.InitiatedBy.App.AppId
$initiatorName = $e.InitiatedBy.App.DisplayName
} else {
# fallback for older SDK shapes
$iu = $e.InitiatedBy.AdditionalProperties['user']
$ia = $e.InitiatedBy.AdditionalProperties['app']
if ($iu) { $initiatorUpn = $iu['userPrincipalName']; $initiatorName = $iu['displayName'] }
elseif ($ia) { $initiatorUpn = $ia['appId']; $initiatorName = $ia['displayName'] }
}
} catch {}
# Targets (the users that were updated)
$targetNames = @()
$targetUpns = @()
foreach ($t in ($e.TargetResources | Where-Object { $_ })) {
try {
if ($t.UserPrincipalName) { $targetUpns += $t.UserPrincipalName }
elseif ($t.AdditionalProperties['userPrincipalName']) { $targetUpns += $t.AdditionalProperties['userPrincipalName'] }
if ($t.DisplayName) { $targetNames += $t.DisplayName }
elseif ($t.AdditionalProperties['displayName']) { $targetNames += $t.AdditionalProperties['displayName'] }
} catch {}
}
# Optional: summarize changed properties
$changedProps = @()
try {
foreach ($d in ($e.AdditionalDetails | Where-Object { $_ })) {
if ($d.Key -and $d.Value) { $changedProps += ("{0}={1}" -f $d.Key, $d.Value) }
}
} catch {}
[PSCustomObject]@{
ActivityDateTime = $e.ActivityDateTime
Activity = $e.ActivityDisplayName
Category = $e.Category
Result = $e.Result
ResultReason = $e.ResultReason
LoggedByService = $e.LoggedByService
CorrelationId = $e.CorrelationId
InitiatedByName = $initiatorName
InitiatedByUPN = $initiatorUpn
TargetNames = ($targetNames -join "; ")
TargetUPNs = ($targetUpns -join "; ")
ChangedDetails = ($changedProps -join "; ")
}
}
# -------- Export to CSV ----------
if (-not (Test-Path -Path $CsvOutDir)) { New-Item -ItemType Directory -Path $CsvOutDir | Out-Null }
$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$csvPath = Join-Path $CsvOutDir ("Entra_Audit_UpdateUser_{0}.csv" -f $ts)
$rows | Sort-Object ActivityDateTime -Descending | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
# -------- Prepare HTML Body ----------
$totalEvents = $rows.Count
$summaryHtml = @"
<html>
<body style='font-family:Segoe UI,Arial,sans-serif'>
<h3>Entra ID Audit Report: "Password Resets By Admin" (Last $DaysBack Days)</h3>
<p>Total events: <b>$totalEvents</b></p>
<p>Time window (UTC): since <b>$sinceIso</b></p>
<p>Attached CSV includes ActivityDateTime, Initiator, Targets, Result, and ChangedDetails (when available).</p>
</body>
</html>
"@
# -------- Prepare Attachment ----------
$fileBytes = [System.IO.File]::ReadAllBytes($csvPath)
$base64Content = [System.Convert]::ToBase64String($fileBytes)
$csvFileName = [System.IO.Path]::GetFileName($csvPath)
$attachment = @{
"@odata.type" = "#microsoft.graph.fileAttachment"
name = $csvFileName
contentBytes = $base64Content
contentType = "text/csv"
}
# -------- Build recipients array (split on ; or ,) ----------
$recipients = @()
$ToList.Split(@(';', ','), [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object {
$addr = $_.Trim()
if ($addr) { $recipients += @{ emailAddress = @{ address = $addr } } }
}
# -------- Prepare and Send Email ----------
$mail = @{
message = @{
subject = "$Subject"
body = @{
contentType = "HTML"
content = $summaryHtml
}
toRecipients = $recipients
attachments = @($attachment)
}
saveToSentItems = $true
}
Send-MgUserMail -UserId $FromUser -BodyParameter $mail
Write-Host "Done. CSV saved at: $csvPath" -ForegroundColor Green
| Error | Cause | Solution |
|---|---|---|
| Authorization_RequestDenied | Missing Graph permissions | Reconnect with AuditLog.Read.All and Mail.Send; ensure admin consent. |
| Get-MgAuditLogDirectoryAudit not recognized | Graph module missing/outdated | Install or update Microsoft.Graph module. |
| Empty CSV | No password reset events in the timeframe | Increase $DaysBack; confirm events exist in Entra admin portal. |
| Email not delivered | $FromUser not mailbox-enabled | Ensure the sender is licensed and mailbox-enabled. |
| Recipient parsing error | Incorrect delimiter handling | Ensure recipients are separated by ; or , (script supports both). |
Password reset by administrators is a critical audit log event—it directly affects user access and security. With this script, you gain automated visibility into who reset which user’s password, when, and why, with results emailed straight to your inbox. By scheduling and enhancing this script, organizations can strengthen their compliance posture and improve identity governance.
© m365corner.com. All Rights Reserved. Design by HTML Codex