Query Recently Updated M365 Users Using Graph PowerShell

Monitoring updates to user accounts in Microsoft 365 (M365) is crucial for maintaining security and compliance in any organization. Changes to user accounts, such as profile updates or password changes, can impact the overall security posture, and it’s important to track who is making these changes and when. In this article, we will create a PowerShell script using the Microsoft Graph API to query Azure AD for recently updated users. The output will display the updated user's email, the admin who performed the update, the time of the update, and the result status (success or failure).

PowerShell Script

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "AuditLog.Read.All" "User.Read.All"

# Fetch all directory audits where users were updated (filter applied to activityDisplayName)
$updatedUsers = Get-MgAuditLogDirectoryAudit -Filter "activityDisplayName eq 'Update user'" -All

# Initialize an array to store results
$results = @()

# Define the cutoff date (last 30 days)
$cutoffDate = (Get-Date).AddDays(-30)

# Iterate through each log entry and capture the details
foreach ($log in $updatedUsers) {
    # Only process logs that are within the last 30 days
    if ([DateTime]$log.activityDateTime -ge $cutoffDate) {
        # Extract the updated time
        $updatedTime = $log.activityDateTime

        # Extract the updated user email (target)
        $updatedUser = $log.targetResources | Where-Object { $_.userPrincipalName } | Select-Object -ExpandProperty userPrincipalName

        # Extract the admin who performed the update (initiated by)
        $updatedBy = $log.initiatedBy.user.userPrincipalName

        # Check the log's status success property or error code to determine result status
        if ($log.status.success -eq $true) {
            $resultStatus = "Success"
        } elseif ($log.status.success -eq $false) {
            $resultStatus = "Failure"
        } else {
            # Fallback to error code check if success property is not present
            $resultStatus = if ($log.status.errorCode -eq 0) { "Success" } else { "Failure" }
        }

        # Add the result to the array
        $results += [pscustomobject]@{
            'Updated Time'  = $updatedTime
            'Updated User'  = $updatedUser
            'Updated By'    = $updatedBy
            'Result Status' = $resultStatus
        }
    }
}

# Display results in table format
$results | Format-Table -AutoSize

How the Script Works

  • Connection to Microsoft Graph: The script uses the Connect-MgGraph cmdlet to authenticate and connect to Microsoft Graph with the necessary permissions (AuditLog.Read.All and User.Read.All).
  • Fetching Audit Logs: The Get-MgAuditLogDirectoryAudit cmdlet retrieves audit logs related to user updates in Azure AD. The filter activityDisplayName eq 'Update user' ensures we only capture user update events.
  • Processing Logs: The script loops through the retrieved logs and checks if the activityDateTime (update time) is within the last 30 days. It extracts key details such as the updated user’s email, the admin responsible for the update, and the result status.
  • Determining Result Status: The result status is determined based on the status.success property. If this property is true, the update was successful; otherwise, it was a failure. If the property is not available, the script defaults to checking the ErrorCode.
  • Displaying Results: The results are displayed in a neatly formatted table showing the updated time, updated user email, admin email, and result status.

Further Enhancing the Script

  • Filtering by Specific User or Admin: You can modify the script to focus on updates made by specific admins or on specific users by adding a filter for initiatedBy.user.userPrincipalName or targetResources.userPrincipalName.
  • Exporting Results to CSV: You can export the results for further analysis by adding an export function:
  • $results | Export-Csv -Path "C:\Path\To\Output.csv" -NoTypeInformation
  • Customizing Time Range: You can adjust the time range by modifying the $cutoffDate variable.

Possible Errors and Solutions

Error: "Invalid filter clause"

Cause: Incorrect or unsupported filter syntax in the -Filter parameter.

Solution: Ensure you use valid OData filters. For instance, date filtering should be done in PowerShell instead of directly in the Graph query.

Error: "Permission Denied"

Cause: Insufficient permissions granted during the connection to Microsoft Graph.

Solution: Ensure that the AuditLog.Read.All and User.Read.All permissions are granted. You may need admin consent for these permissions.

Error: "Request_UnsupportedQuery"

Cause: Some properties may not be filterable or searchable directly using the -Filter parameter.

Solution: Test queries with supported properties and check the Microsoft Graph API documentation for any limitations.

Conclusion

Tracking user account updates in Microsoft 365 is an important task for security and compliance monitoring. This PowerShell script leverages Microsoft Graph to make it easy to pull audit logs and list recent updates in a structured format. With simple modifications, you can enhance the script to suit your specific needs, such as filtering by users or exporting the data for reporting purposes. Regularly running this script will give you better insight into how and when M365 accounts are being modified as well as who is making the changes.

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