Track Users Added to Admin Roles in the Last 30 Days Using Microsoft Graph PowerShell

Privileged role assignments are among the most sensitive changes in any Microsoft 365 tenant. A user being added to an admin role can be intentional (temporary elevation, onboarding into IT), but it can also signal privilege creep or even malicious escalation.

To maintain strong governance and ensure ongoing visibility, administrators should regularly review who received admin roles recently, who assigned them, and when those assignments happened.

This Graph PowerShell script queries Entra ID directory audit logs, collects all admin role assignment events from the past 30 days, exports the results to CSV, and emails the report automatically to administrators or stakeholders.

🚀 Community Edition Released!

Try the M365Corner Microsoft 365 Reporting Tool — your DIY pack with 20+ out-of-the-box M365 reports for Users, Groups, and Teams.


i) The Script

$SenderUPN = "admin@yourtenant.onmicrosoft.com"
$Recipients = @(
    "admin@yourtenant.onmicrosoft.com",
    "securityteam@yourtenant.onmicrosoft.com"
)
Connect-MgGraph -Scopes "AuditLog.Read.All","Directory.Read.All","User.Read.All","Mail.Send"
$StartDate = (Get-Date).ToUniversalTime().AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")
$AuditLogs = Get-MgAuditLogDirectoryAudit -All -Filter "activityDateTime ge $StartDate"

$RoleAddEvents = $AuditLogs | Where-Object {
    $_.ActivityDisplayName -match "Add member to role|Add eligible member to role|Add user to role"
}

$Report = foreach ($log in $RoleAddEvents) {

    $RoleTarget = $log.TargetResources | Where-Object { $_.Type -eq "Role" } | Select-Object -First 1
    $UserTarget = $log.TargetResources | Where-Object { $_.Type -eq "User" } | Select-Object -First 1

    if (-not $UserTarget -or -not $RoleTarget) { continue }

    $ActorUPN = $null
    if ($log.InitiatedBy.User) {
        $ActorUPN = $log.InitiatedBy.User.UserPrincipalName
    }

    [PSCustomObject]@{
        "Admin/User Name"        = $UserTarget.DisplayName
        "Admin/User UPN"         = $UserTarget.UserPrincipalName
        "Role Assigned"          = $RoleTarget.DisplayName
        "Assigned By"            = $ActorUPN
        "Assignment DateTime"    = $log.ActivityDateTime
        "Result"                 = $log.Result
    }
}

$ReportPath = "$env:TEMP\AdminRolesAdded_Last30Days.csv"
$Report | Sort-Object "Assignment DateTime" -Descending | Export-Csv -Path $ReportPath -NoTypeInformation -Encoding UTF8

$Count = @($Report).Count
$Subject = "Admin Role Assignments (Last 30 Days) — $(Get-Date -Format 'yyyy-MM-dd')"

$Body = @"
Hello Team,

Attached is the Admin Role Assignments Report for the last 30 days.
This report shows users who were newly added to Entra ID admin roles.

Total new admin assignments found: $Count

Regards,
Graph PowerShell Automation "@ $AttachmentContent = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($ReportPath)) $Attachments = @( @{ "@odata.type" = "#microsoft.graph.fileAttachment" Name = "AdminRolesAdded_Last30Days.csv" ContentBytes = $AttachmentContent } ) $ToRecipients = $Recipients | ForEach-Object { @{ EmailAddress = @{ Address = $_ } } } $Message = @{ Message = @{ Subject = $Subject Body = @{ ContentType = "HTML" Content = $Body } ToRecipients = $ToRecipients Attachments = $Attachments } SaveToSentItems = "true" } Send-MgUserMail -UserId $SenderUPN -BodyParameter $Message Write-Host "Admin assignments report emailed successfully." -ForegroundColor Green

ii) How the Script Works

  1. Connects to Microsoft Graph
  2. The script connects using these delegated scopes:

    • AuditLog.Read.All → read directory audit logs
    • Directory.Read.All → resolve role and directory objects
    • User.Read.All → resolve user identity details
    • Mail.Send → email report to stakeholders

    These permissions must be approved with admin consent.

  3. Defines a 30-Day Audit Window
  4. A start date is calculated for the last 30 days and formatted in UTC:

    $StartDate = (Get-Date).ToUniversalTime().AddDays(-30)

    Only audit events newer than this are pulled.

  5. Retrieves Directory Audit Logs
  6. The script fetches directory audit logs using:

    Get-MgAuditLogDirectoryAudit -Filter "activityDateTime ge $StartDate"

    This returns all directory actions during the window.

  7. Filters Role Assignment Events
  8. Audit logs contain many change types, so the script filters only:

    • Add member to role
    • Add eligible member to role
    • Add user to role

    These represent new privileged role additions.

  9. Extracts Role, User, and Actor
  10. For each matching audit record:

    • the role object is located inside TargetResources
    • the user object is located inside TargetResources
    • the initiator (actor) is pulled from InitiatedBy.User
  11. Generates a Report and Emails It
  12. A structured report is created, exported to CSV, base64-encoded, and sent to configured recipients using Send-MgUserMail.


iii) Further Enhancements

  • Filter for specific high-risk roles
  • Example:

    • Global Administrator
    • Privileged Role Administrator
    • Security Administrator
      Only report assignments to these roles.
  • Add removal events too
  • Track:

    • who was added
    • who was removed
      in a combined lifecycle report.
  • Track eligibility vs permanent assignments
  • Separate:

    • PIM eligible additions
    • direct permanent additions
  • Upload report to SharePoint
  • Archive role assignment history for audits.

  • Auto-alert on Global Admin changes
  • Trigger a Teams or email alert immediately if Global Admin is assigned.


iv) Possible Errors & Solutions

Error Cause Solution
Authorization_RequestDenied Missing permissions for audit logs. Ensure scopes include: AuditLog.Read.All, Directory.Read.All and User.Read.All permissions. Consent must be granted by an admin.
Empty CSV Report No role assignment events occurred during the last 30 days. This is valid behavior. Increase window (e.g., 60 days) to verify.
Actor UPN appears blank Some role assignments are system-driven or done by non-user principals. Keep the field; it indicates automated assignment.
Throttling or large tenant delay Very large tenants may hit Graph throttling. Rerun after a short pause or add paging/backoff logic.


v) Conclusion

Tracking new privileged role assignments is critical for preventing privilege creep and ensuring admin governance stays transparent and controlled. This script automates that visibility by extracting admin role additions from directory audit logs, generating a clean CSV, and emailing it to stakeholders for review.

Scheduling this report weekly or monthly provides continuous security assurance and helps teams stay audit-ready at all times.


Graph PowerShell Explorer Widget

20 Graph PowerShell cmdlets with easily accessible "working" examples.


Permission Required

Example:


                


                


                

© m365corner.com. All Rights Reserved. Design by HTML Codex