Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.
🚀 Launch ToolkitWhen new groups are created in Entra ID, they can open the door to collaboration—or, if unchecked, to risks like overexposed data or unmonitored access. Tracking the “Add group” activity in audit logs helps administrators ensure that only authorized people are creating groups, and that every new group is visible to IT and security. This script automates the process by querying Entra ID Audit Logs for “Add group” events, exporting them to CSV, and emailing the results to administrators or stakeholders.
# ===== Entra ID Audit: "Add group" (GroupManagement) -> CSV -> Email =====
# Requires: Microsoft.Graph module
# Scopes: AuditLog.Read.All, Mail.Send
# -------- Email & Time Window ----------
$FromUser = "admin@contoso.com" # Sender (must have mailbox)
$ToList = "it-ops@contoso.com;secops@contoso.com" # Recipients (; or , separated)
$Subject = 'Entra ID Audit: "Add group" Activity Report'
$DaysBack = 7 # Lookback window (days)
$CsvOutDir = "$env:TEMP"
# -------- Connect to Microsoft Graph ----------
Import-Module Microsoft.Graph -ErrorAction Stop
Connect-MgGraph -Scopes "AuditLog.Read.All","Mail.Send"
# -------- Build filter (UTC ISO format) ----------
$sinceIso = (Get-Date).ToUniversalTime().AddDays(-1 * $DaysBack).ToString("o")
$filter = "category eq 'GroupManagement' and activityDisplayName eq 'Add group' 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 groups that were added)
$targetNames = @()
$targetIds = @()
foreach ($t in ($e.TargetResources | Where-Object { $_ })) {
try {
if ($t.DisplayName) { $targetNames += $t.DisplayName }
elseif ($t.AdditionalProperties['displayName']) { $targetNames += $t.AdditionalProperties['displayName'] }
if ($t.Id) { $targetIds += $t.Id }
elseif ($t.AdditionalProperties['id']) { $targetIds += $t.AdditionalProperties['id'] }
} catch {}
}
# Optional: pick a few additional details if present (e.g., groupType, visibility)
$details = @()
try {
foreach ($d in ($e.AdditionalDetails | Where-Object { $_ })) {
if ($d.Key -and $d.Value) { $details += ("{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
TargetGroupNames = ($targetNames -join "; ")
TargetGroupIds = ($targetIds -join "; ")
AdditionalInfo = ($details -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_AddGroup_{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: "Add group" (Last $DaysBack Days)</h3>
<p>Total events: <b>$totalEvents</b></p>
<p>Time window since (UTC): since <b>$sinceIso</b></p>
<p>Attached CSV includes ActivityDateTime, Initiator, TargetGroupNames/Ids, Result, and AdditionalInfo.</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 | Module not installed/outdated | Install or update Microsoft.Graph. |
| Empty CSV | No “Add group” events in the timeframe | Extend $DaysBack or validate in Entra Admin Center. |
| Email not sent | $FromUser not mailbox-enabled | Ensure the sender has a valid mailbox. |
| Split error in recipients | Incorrect delimiter usage | Use ; or , (both supported by script). |
Monitoring group creation is a cornerstone of identity governance. With this Graph PowerShell script, admins can automatically track “Add group” events in Entra ID, export the details to CSV, and deliver them directly to stakeholders. By scheduling it regularly and extending it with filters or SIEM integration, organizations can ensure group creation is always audited, transparent, and controlled.
© m365corner.com. All Rights Reserved. Design by HTML Codex