Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Tracking Device Wipes in Microsoft Intune


Managing devices in Intune often involves remote actions like wiping lost or retired devices. While Intune makes these actions straightforward, tracking and auditing them can be confusing, especially if the device record is deleted after the wipe. In this post, we’ll walk through how to trace a wiped device using the Intune admin portal, tenant audit logs, and the Device Actions report.

1. Tenant Admin Portal: Where It All Begins

When you initiate a device wipe, Intune logs the activity in the Tenant Audit Logs. You can access these logs via:

Intune Admin Center → Tenant administration → Audit logs

Filter by:

  • Category: Device
  • Activity Type: wipe ManagedDevice
  • Initiated By: The user who triggered the wipe

Here’s an example log entry:

Activity: wipe ManagedDevice
Date: Fri, 03 Oct 2025 10:02:52 GMT
Initiated By: mr.wipe@croucher.cloud
Target ObjectID: a1b6a0d6-5f55-4e45-9baa-ded1e0700b55
Status: Success

At first glance, the log gives you the ObjectID of the device and the user who initiated the wipe. However, it does not directly show the device name, which can be confusing if the device has been deleted.

2. Understanding Audit Logs and the Wipe Action

The wipe ManagedDevice activity is recorded as an action in the audit logs, including details such as:

  • Operation Type: Action
  • Status: Success or Failure
  • Modified Properties: For example, keepEnrollmentData = False

The ObjectID uniquely identifies the device in Intune. If the device still exists, you could search for this ID in Devices → All devices. But if the device has been fully wiped and the record removed, the ObjectID no longer resolves to a live device.

3. Device Name After Deletion: The Device Actions Report

Here’s where Intune makes up for that limitation. Even if the device record is gone, you can still find its friendly name via:

Intune Admin Center → Devices → Monitor → Device Actions

Steps:

  1. Filter by Action = Wipe
  2. Filter by Initiated By = the user who triggered the wipe
  3. Look at the Device Name column

The Device Actions report retains historical information, including the device name, user, and timestamp. This allows you to correlate the ObjectID in the audit logs with the actual device name before deletion.

4. Correlation IDs: The Quirk

One interesting observation is that the Correlation ID in the tenant audit log for the wipe request does not match the correlation ID in the Entra (Azure AD) audit logs.

Why?

  • Intune generates its own correlation ID for device actions internally.
  • Entra/Azure AD generates a separate correlation ID for directory-level events.
  • These IDs are not synchronized, which means you cannot rely on a single correlation ID to trace an action across both systems. Instead, use the ObjectID / Device Name / Timestamp as the common thread.

Script and HTML Report

This looks like a good opportunity to take the information above and using a App Registration create a Powershell script to complete the actions above and then produce a nice HTML report along with a CSV file as well.

API Permissions required

DeviceManagementManagedDevices.Read.All - Read managed devices
DeviceManagementServiceConfig.Read.All - Read Intune configuration
DeviceManagementConfiguration.Read.All - Read device configurations
AuditLog.Read.All - Read directory audit logs
User.Read.All - Read user profiles

Script : wipe-report.ps1

Activity: wipe ManagedDevice
#Requires -Version 5.1
<#
.SYNOPSIS
    Intune Device Wipe Report - Finding Initiators Including Allan Young
.DESCRIPTION
    Uses multiple methods to find who initiated wipes, including beta endpoints
#>

# ============================================================================
# CONFIGURATION
# ============================================================================
$TenantId = "<tenant-id>"
$ClientId = "<client-id>"
$ClientSecret = "<client-secret>"
$Days = 30
$OutputPath = (Get-Location).Path

# ============================================================================
# MAIN SCRIPT - MULTIPLE METHODS TO FIND INITIATORS
# ============================================================================

Write-Host "`n╔══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║   Intune Device Wipe Report - Finding All Initiators      ║" -ForegroundColor Cyan  
Write-Host "╚══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan

try {
    # Get token
    Write-Host "`nStep 1: Authenticating..." -ForegroundColor Yellow
    $body = @{
        client_id     = $ClientId
        client_secret = $ClientSecret
        scope         = "https://graph.microsoft.com/.default"
        grant_type    = "client_credentials"
    }
    
    $tokenResponse = Invoke-RestMethod `
        -Method Post `
        -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" `
        -Body $body `
        -ContentType "application/x-www-form-urlencoded"
    
    $headers = @{
        "Authorization" = "Bearer $($tokenResponse.access_token)"
    }
    
    Write-Host "✓ Authentication successful" -ForegroundColor Green
    
    # Method 1: Try beta remoteActionAudits endpoint
    Write-Host "`nStep 2: Trying to get remote action audits (beta)..." -ForegroundColor Yellow
    $remoteActions = @()
    
    try {
        $uri = "https://graph.microsoft.com/beta/deviceManagement/remoteActionAudits"
        $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET -ErrorAction SilentlyContinue
        
        if ($response.value) {
            $remoteActions = $response.value
            Write-Host "✓ Retrieved $($remoteActions.Count) remote action audits!" -ForegroundColor Green
            
            # Show sample of what we found
            $wipeActions = $remoteActions | Where-Object { $_.action -match "wipe|retire" }
            if ($wipeActions) {
                Write-Host "  Found $($wipeActions.Count) wipe/retire actions" -ForegroundColor Cyan
                $initiators = $wipeActions | Select-Object -ExpandProperty initiatedByUserPrincipalName -Unique
                Write-Host "  Initiators found: $($initiators -join ', ')" -ForegroundColor Green
            }
        }
    }
    catch {
        Write-Host "  ✗ Cannot access remoteActionAudits (beta)" -ForegroundColor Yellow
    }
    
    # Method 2: Try v1.0 with expanded query
    if ($remoteActions.Count -eq 0) {
        Write-Host "`nTrying deviceManagement audit events with different approach..." -ForegroundColor Yellow
        
        try {
            # Try without filter first
            $uri = "https://graph.microsoft.com/v1.0/deviceManagement/auditEvents"
            $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET -ErrorAction SilentlyContinue -TimeoutSec 5
            
            if ($response.value) {
                Write-Host "✓ SUCCESS! Retrieved $($response.value.Count) Intune audit events!" -ForegroundColor Green
                
                # Get all pages
                $allAuditEvents = @()
                $allAuditEvents += $response.value
                
                while ($response.'@odata.nextLink') {
                    $response = Invoke-RestMethod -Uri $response.'@odata.nextLink' -Headers $headers -Method GET
                    $allAuditEvents += $response.value
                    Write-Host "  Retrieved $($allAuditEvents.Count) events so far..." -ForegroundColor Gray
                }
                
                # Filter for wipe events
                $wipeEvents = $allAuditEvents | Where-Object {
                    $_.activity -match "wipe|retire" -or
                    $_.activityDisplayName -match "wipe|retire" -or
                    $_.activityType -match "wipe|retire"
                }
                
                Write-Host "✓ Found $($wipeEvents.Count) wipe/retire events in Intune audit!" -ForegroundColor Green
            }
        }
        catch {
            Write-Host "  ✗ Still cannot access deviceManagement/auditEvents" -ForegroundColor Yellow
        }
    }
    
    # Method 3: Get all managed devices
    Write-Host "`nStep 3: Getting all managed devices..." -ForegroundColor Yellow
    $allDevices = @()
    $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices"
    
    do {
        $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET
        if ($response.value) {
            $allDevices += $response.value
        }
        $uri = $response.'@odata.nextLink'
    } while ($uri)
    
    Write-Host "✓ Retrieved $($allDevices.Count) managed devices" -ForegroundColor Green
    
    # Method 4: Try directory audit logs for device operations
    Write-Host "`nStep 4: Checking directory audit logs for device operations..." -ForegroundColor Yellow
    $directoryAudits = @()
    
    # Search for device-related audit events
    $filters = @(
        "targetResources/any(t: t/type eq 'Device')",
        "category eq 'Device'"
    )
    
    foreach ($filter in $filters) {
        try {
            $encodedFilter = [System.Web.HttpUtility]::UrlEncode($filter)
            $uri = "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?`$filter=$encodedFilter&`$top=999"
            
            $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET -ErrorAction SilentlyContinue -TimeoutSec 10
            
            if ($response.value) {
                $directoryAudits += $response.value
            }
        }
        catch {
            # Continue with next filter
        }
    }
    
    # Also get recent directory audits without filter
    try {
        $startDate = (Get-Date).AddDays(-$Days).ToString("yyyy-MM-dd")
        $uri = "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?`$filter=activityDateTime ge $startDate&`$top=999"
        
        $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET -TimeoutSec 10
        
        if ($response.value) {
            $deviceAudits = $response.value | Where-Object {
                $_.category -eq "Device" -or
                $_.activityDisplayName -match "device|wipe|retire" -or
                ($_.targetResources -and $_.targetResources.type -contains "Device")
            }
            $directoryAudits += $deviceAudits
        }
    }
    catch {
        # Continue
    }
    
    Write-Host "✓ Retrieved $($directoryAudits.Count) directory audit events" -ForegroundColor Green
    
    # Process all data sources
    Write-Host "`nStep 5: Processing and combining all data sources..." -ForegroundColor Yellow
    $reportData = @()
    $cutoffDate = (Get-Date).AddDays(-$Days)
    
    # Process remote action audits if we have them
    if ($remoteActions.Count -gt 0) {
        foreach ($action in $remoteActions) {
            if ($action.action -match "wipe|retire|delete|remoteLock|factoryReset") {
                $actionDate = if ($action.requestDateTime) { [DateTime]$action.requestDateTime } else { [DateTime]::MinValue }
                
                if ($actionDate -ge $cutoffDate) {
                    $reportData += [PSCustomObject]@{
                        DateTime = $actionDate.ToString("yyyy-MM-dd HH:mm:ss")
                        DeviceName = $action.deviceDisplayName
                        DeviceId = $action.managedDeviceId
                        UserEmail = $action.userPrincipalName
                        Action = $action.action
                        Status = $action.actionState
                        InitiatedBy = $action.initiatedByUserPrincipalName
                        RequestId = $action.id
                        Source = "Remote Action Audit"
                    }
                }
            }
        }
        
        Write-Host "✓ Processed $($reportData.Count) remote action audits with initiators!" -ForegroundColor Green
    }
    
    # Process Intune audit events if we have them
    if ($wipeEvents.Count -gt 0) {
        foreach ($event in $wipeEvents) {
            $eventDate = [DateTime]$event.activityDateTime
            
            if ($eventDate -ge $cutoffDate) {
                $initiator = "Unknown"
                if ($event.actor) {
                    if ($event.actor.userPrincipalName) {
                        $initiator = $event.actor.userPrincipalName
                    } elseif ($event.actor.applicationDisplayName) {
                        $initiator = $event.actor.applicationDisplayName
                    }
                }
                
                $deviceName = "Unknown"
                $deviceId = ""
                if ($event.resources) {
                    $deviceResource = $event.resources | Where-Object { $_.type -like "*Device*" } | Select-Object -First 1
                    if ($deviceResource) {
                        $deviceName = $deviceResource.displayName
                        $deviceId = $deviceResource.resourceId
                    }
                }
                
                # Check if we already have this from remote actions
                $exists = $reportData | Where-Object {
                    $_.DeviceId -eq $deviceId -and
                    [Math]::Abs(([DateTime]$_.DateTime - $eventDate).TotalMinutes) -lt 60
                }
                
                if (-not $exists) {
                    $reportData += [PSCustomObject]@{
                        DateTime = $eventDate.ToString("yyyy-MM-dd HH:mm:ss")
                        DeviceName = $deviceName
                        DeviceId = $deviceId
                        UserEmail = ""
                        Action = if ($event.activity) { $event.activity } else { $event.activityDisplayName }
                        Status = if ($event.activityResult) { $event.activityResult } else { "Success" }
                        InitiatedBy = $initiator
                        RequestId = $event.id
                        Source = "Intune Audit"
                    }
                }
            }
        }
        
        Write-Host "✓ Processed Intune audit events with initiators!" -ForegroundColor Green
    }
    
    # Process directory audits
    $initiatorLookup = @{}
    foreach ($audit in $directoryAudits) {
        if ($audit.initiatedBy -and $audit.targetResources) {
            foreach ($resource in $audit.targetResources) {
                if ($resource.type -eq "Device" -and $resource.displayName) {
                    $key = $resource.displayName.ToLower()
                    $initiator = "Unknown"
                    
                    if ($audit.initiatedBy.user.userPrincipalName) {
                        $initiator = $audit.initiatedBy.user.userPrincipalName
                    } elseif ($audit.initiatedBy.app.displayName) {
                        $initiator = $audit.initiatedBy.app.displayName
                    }
                    
                    $initiatorLookup[$key] = @{
                        Initiator = $initiator
                        Date = [DateTime]$audit.activityDateTime
                        Activity = $audit.activityDisplayName
                    }
                }
            }
        }
    }
    
    Write-Host "  Built initiator lookup with $($initiatorLookup.Count) entries" -ForegroundColor Gray
    
    # Process devices with wipe/retire status
    foreach ($device in $allDevices) {
        # Check device action results
        if ($device.deviceActionResults -and $device.deviceActionResults.Count -gt 0) {
            foreach ($action in $device.deviceActionResults) {
                if ($action.actionName -match "wipe|retire|delete|remoteLock|factoryReset") {
                    $actionDate = [DateTime]$action.startDateTime
                    
                    if ($actionDate -ge $cutoffDate) {
                        # Try to find initiator
                        $initiator = "Unknown"
                        $deviceNameLower = $device.deviceName.ToLower()
                        
                        # Check our lookup table
                        if ($initiatorLookup.ContainsKey($deviceNameLower)) {
                            $lookupInfo = $initiatorLookup[$deviceNameLower]
                            # Check if dates are close (within 24 hours)
                            if ([Math]::Abs(($lookupInfo.Date - $actionDate).TotalHours) -lt 24) {
                                $initiator = $lookupInfo.Initiator
                            }
                        }
                        
                        # Check if we already have this event
                        $exists = $reportData | Where-Object {
                            $_.DeviceName -eq $device.deviceName -and
                            [Math]::Abs(([DateTime]$_.DateTime - $actionDate).TotalMinutes) -lt 60
                        }
                        
                        if (-not $exists) {
                            $reportData += [PSCustomObject]@{
                                DateTime = $actionDate.ToString("yyyy-MM-dd HH:mm:ss")
                                DeviceName = $device.deviceName
                                DeviceId = $device.id
                                UserEmail = $device.userPrincipalName
                                Action = $action.actionName
                                Status = $action.actionState
                                InitiatedBy = $initiator
                                RequestId = ""
                                Source = "Device Action"
                            }
                        }
                    }
                }
            }
        }
        
        # Check management state
        if ($device.managementState -match "wipe|retire") {
            $exists = $reportData | Where-Object { $_.DeviceName -eq $device.deviceName }
            
            if (-not $exists) {
                $initiator = "Unknown"
                $deviceNameLower = $device.deviceName.ToLower()
                
                if ($initiatorLookup.ContainsKey($deviceNameLower)) {
                    $initiator = $initiatorLookup[$deviceNameLower].Initiator
                }
                
                $reportData += [PSCustomObject]@{
                    DateTime = ([DateTime]$device.lastSyncDateTime).ToString("yyyy-MM-dd HH:mm:ss")
                    DeviceName = $device.deviceName
                    DeviceId = $device.id
                    UserEmail = $device.userPrincipalName
                    Action = "Device " + $device.managementState
                    Status = "Completed"
                    InitiatedBy = $initiator
                    RequestId = ""
                    Source = "Device State"
                }
            }
        }
    }
    
    # Sort by date
    $reportData = $reportData | Sort-Object DateTime -Descending
    
    # Statistics
    $totalEvents = $reportData.Count
    $knownInitiators = ($reportData | Where-Object { $_.InitiatedBy -ne "Unknown" }).Count
    $unknownInitiators = ($reportData | Where-Object { $_.InitiatedBy -eq "Unknown" }).Count
    $uniqueInitiators = $reportData | Where-Object { $_.InitiatedBy -ne "Unknown" } | Select-Object -ExpandProperty InitiatedBy -Unique
    
    Write-Host "`n╔══════════════════════════════════════════════════════════╗" -ForegroundColor Green
    Write-Host "║                    RESULTS SUMMARY                        ║" -ForegroundColor Green
    Write-Host "╚══════════════════════════════════════════════════════════╝" -ForegroundColor Green
    
    Write-Host "`n📊 Statistics:" -ForegroundColor Cyan
    Write-Host "   • Total wipe/retire events: $totalEvents" -ForegroundColor White
    Write-Host "   • Events with known initiator: $knownInitiators" -ForegroundColor Green
    Write-Host "   • Events with unknown initiator: $unknownInitiators" -ForegroundColor Yellow
    
    if ($uniqueInitiators.Count -gt 0) {
        Write-Host "`n👥 Initiators Found:" -ForegroundColor Cyan
        foreach ($initiator in $uniqueInitiators) {
            $count = ($reportData | Where-Object { $_.InitiatedBy -eq $initiator }).Count
            Write-Host "   • $initiator ($count events)" -ForegroundColor White
        }
    }
    
    # Generate detailed HTML report
    Write-Host "`nStep 6: Generating HTML report..." -ForegroundColor Yellow
    
    $html = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Intune Device Wipe Report - With Initiators</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { 
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #f5f5f7;
            padding: 20px;
        }
        .container {
            max-width: 1800px;
            margin: 0 auto;
            background: white;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.08);
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 40px;
            text-align: center;
        }
        .header h1 {
            font-size: 2.5em;
            font-weight: 300;
            margin-bottom: 10px;
            text-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .header .subtitle {
            opacity: 0.95;
            font-size: 1.1em;
        }
        .stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 20px;
            padding: 30px;
            background: linear-gradient(135deg, #667eea10 0%, #764ba210 100%);
            border-bottom: 1px solid #e1e4e8;
        }
        .stat {
            background: white;
            padding: 25px;
            border-radius: 10px;
            text-align: center;
            box-shadow: 0 2px 8px rgba(0,0,0,0.05);
            border: 1px solid #e1e4e8;
            transition: transform 0.2s;
        }
        .stat:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        .stat-value {
            font-size: 2.8em;
            font-weight: 700;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            display: block;
        }
        .stat-label {
            font-size: 0.9em;
            color: #586069;
            margin-top: 8px;
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        .initiators-section {
            margin: 20px 30px;
            padding: 20px;
            background: #f8f9fa;
            border-radius: 8px;
            border-left: 4px solid #667eea;
        }
        .initiators-section h3 {
            color: #667eea;
            margin-bottom: 15px;
        }
        .initiator-item {
            display: inline-block;
            margin: 5px;
            padding: 8px 16px;
            background: white;
            border-radius: 20px;
            border: 1px solid #e1e4e8;
            font-size: 0.95em;
        }
        .initiator-count {
            display: inline-block;
            margin-left: 8px;
            padding: 2px 8px;
            background: #667eea;
            color: white;
            border-radius: 10px;
            font-size: 0.85em;
            font-weight: 600;
        }
        .search-container {
            padding: 30px;
            background: white;
        }
        .search-box {
            width: 100%;
            padding: 15px 25px;
            font-size: 16px;
            border: 2px solid #e1e4e8;
            border-radius: 50px;
            transition: all 0.3s ease;
        }
        .search-box:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102,126,234,0.1);
        }
        .warning-box {
            margin: 20px 30px;
            padding: 15px 20px;
            background: #fff3cd;
            border-left: 4px solid #ffc107;
            border-radius: 4px;
            color: #856404;
        }
        .success-box {
            margin: 20px 30px;
            padding: 15px 20px;
            background: #d4edda;
            border-left: 4px solid #28a745;
            border-radius: 4px;
            color: #155724;
        }
        .table-container {
            overflow-x: auto;
            padding: 0 30px 30px 30px;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            font-size: 0.9em;
        }
        th {
            background: #f6f8fa;
            padding: 14px 8px;
            text-align: left;
            font-weight: 600;
            font-size: 0.85em;
            color: #586069;
            text-transform: uppercase;
            letter-spacing: 0.3px;
            border-bottom: 2px solid #667eea;
            position: sticky;
            top: 0;
            z-index: 10;
            cursor: pointer;
            white-space: nowrap;
        }
        th:hover {
            background: #e9ecef;
        }
        td {
            padding: 12px 8px;
            border-bottom: 1px solid #f0f3f6;
            color: #24292e;
        }
        tr:hover {
            background: #f8f9fb;
        }
        .device-name {
            font-weight: 600;
            color: #667eea;
        }
        .initiator-known {
            font-weight: 600;
            color: #28a745;
        }
        .initiator-unknown {
            color: #dc3545;
            font-style: italic;
        }
        .status-done, .status-Completed, .status-Success {
            color: #28a745;
            font-weight: 600;
        }
        .status-failed {
            color: #dc3545;
            font-weight: 600;
        }
        .status-pending {
            color: #ffc107;
            font-weight: 600;
        }
        .source-badge {
            display: inline-block;
            padding: 3px 10px;
            border-radius: 12px;
            font-size: 0.8em;
            font-weight: 600;
            text-transform: uppercase;
        }
        .source-remote { background: #e3f2fd; color: #1976d2; }
        .source-intune { background: #f3e5f5; color: #7b1fa2; }
        .source-device { background: #e8f5e9; color: #388e3c; }
        .source-directory { background: #fff3e0; color: #f57c00; }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🔍 Intune Device Wipe Report</h1>
            <div class="subtitle">
                Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") | Last $Days Days<br>
                Complete Analysis with Initiator Discovery
            </div>
        </div>
        
        <div class="stats">
            <div class="stat">
                <span class="stat-value">$totalEvents</span>
                <div class="stat-label">Total Events</div>
            </div>
            <div class="stat">
                <span class="stat-value">$knownInitiators</span>
                <div class="stat-label">Known Initiators</div>
            </div>
            <div class="stat">
                <span class="stat-value">$unknownInitiators</span>
                <div class="stat-label">Unknown Initiators</div>
            </div>
            <div class="stat">
                <span class="stat-value">$($uniqueInitiators.Count)</span>
                <div class="stat-label">Unique Admins</div>
            </div>
        </div>
"@

    # Add initiators section if we found any
    if ($uniqueInitiators.Count -gt 0) {
        $html += @"
        <div class="initiators-section">
            <h3>👥 Administrators Who Performed Wipes:</h3>
            <div>
"@
        foreach ($initiator in $uniqueInitiators) {
            $count = ($reportData | Where-Object { $_.InitiatedBy -eq $initiator }).Count
            $html += @"
                <span class="initiator-item">
                    $initiator <span class="initiator-count">$count</span>
                </span>
"@
        }
        $html += @"
            </div>
        </div>
"@
    } else {
        $html += @"
        <div class="warning-box">
            <strong>⚠ Warning:</strong> Could not retrieve initiator information. The Intune audit events API appears to be blocked.
            Please check the Intune portal directly at: Tenant Administration → Audit logs
        </div>
"@
    }

    $html += @"
        <div class="search-container">
            <input type="text" class="search-box" id="searchInput" placeholder="🔍 Search by device name, user, initiator, date, or any field..." onkeyup="filterTable()">
        </div>
        
        <div class="table-container">
"@

    if ($reportData.Count -gt 0) {
        $html += @"
            <table id="dataTable">
                <thead>
                    <tr>
                        <th onclick="sortTable(0)">Date/Time ↕</th>
                        <th onclick="sortTable(1)">Device Name ↕</th>
                        <th onclick="sortTable(2)">User Email ↕</th>
                        <th onclick="sortTable(3)">Action ↕</th>
                        <th onclick="sortTable(4)">Status ↕</th>
                        <th onclick="sortTable(5)">Initiated By ↕</th>
                        <th onclick="sortTable(6)">Source ↕</th>
                    </tr>
                </thead>
                <tbody>
"@
        
        foreach ($row in $reportData) {
            $statusClass = "status-$($row.Status)"
            $initiatorClass = if ($row.InitiatedBy -eq "Unknown") { "initiator-unknown" } else { "initiator-known" }
            
            # Determine source badge class
            $sourceClass = "source-device"
            if ($row.Source -like "*Remote*") { $sourceClass = "source-remote" }
            elseif ($row.Source -like "*Intune*") { $sourceClass = "source-intune" }
            elseif ($row.Source -like "*Directory*") { $sourceClass = "source-directory" }
            
            $html += @"
                    <tr>
                        <td>$($row.DateTime)</td>
                        <td class="device-name">$($row.DeviceName)</td>
                        <td>$($row.UserEmail)</td>
                        <td>$($row.Action)</td>
                        <td class="$statusClass">$($row.Status)</td>
                        <td class="$initiatorClass">$($row.InitiatedBy)</td>
                        <td><span class="source-badge $sourceClass">$($row.Source)</span></td>
                    </tr>
"@
        }
        
        $html += "</tbody></table>"
    } else {
        $html += "<div style='text-align: center; padding: 40px; color: #999;'>No wipe or retire events found in the last $Days days</div>"
    }
    
    $html += @"
        </div>
    </div>
    
    <script>
        function filterTable() {
            const input = document.getElementById('searchInput');
            const filter = input.value.toUpperCase();
            const table = document.getElementById('dataTable');
            if (!table) return;
            
            const tbody = table.getElementsByTagName('tbody')[0];
            const rows = tbody.getElementsByTagName('tr');
            
            for (let i = 0; i < rows.length; i++) {
                const cells = rows[i].getElementsByTagName('td');
                let found = false;
                
                for (let j = 0; j < cells.length; j++) {
                    if (cells[j].textContent.toUpperCase().indexOf(filter) > -1) {
                        found = true;
                        break;
                    }
                }
                rows[i].style.display = found ? '' : 'none';
            }
        }
        
        let sortDir = {};
        function sortTable(n) {
            const table = document.getElementById('dataTable');
            if (!table) return;
            
            const tbody = table.getElementsByTagName('tbody')[0];
            const rows = Array.from(tbody.getElementsByTagName('tr'));
            
            sortDir[n] = !sortDir[n];
            
            rows.sort((a, b) => {
                const x = a.getElementsByTagName('td')[n].textContent.toLowerCase();
                const y = b.getElementsByTagName('td')[n].textContent.toLowerCase();
                
                if (n === 0) { // Date column
                    return sortDir[n] 
                        ? new Date(x) - new Date(y)
                        : new Date(y) - new Date(x);
                }
                return sortDir[n] ? x.localeCompare(y) : y.localeCompare(x);
            });
            
            rows.forEach(row => tbody.appendChild(row));
        }
    </script>
</body>
</html>
"@
    
    # Save HTML report
    $htmlPath = Join-Path $OutputPath "IntuneWipeReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
    $html | Out-File -FilePath $htmlPath -Encoding UTF8
    
    # Also save CSV for data analysis
    $csvPath = Join-Path $OutputPath "IntuneWipeReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    $reportData | Export-Csv -Path $csvPath -NoTypeInformation
    
    Write-Host "`n✓ Reports generated:" -ForegroundColor Green
    Write-Host "   • HTML: $htmlPath" -ForegroundColor White
    Write-Host "   • CSV: $csvPath" -ForegroundColor White
    
    # Show sample of data
    Write-Host "`nSample of wipe events:" -ForegroundColor Cyan
    $reportData | Select-Object -First 5 | Format-Table DateTime, DeviceName, Action, InitiatedBy, Source -AutoSize
    
    if ($knownInitiators -eq 0) {
        Write-Host "`n⚠ WARNING: Could not retrieve initiator information!" -ForegroundColor Red
        Write-Host "  This indicates the Intune audit events API is blocked." -ForegroundColor Yellow
        Write-Host "  You'll need to check the Intune portal manually for initiator details." -ForegroundColor Yellow
    }
}
catch {
    Write-Host "`n❌ ERROR: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Gray
}

Visual Results

This is the resulting report from the Powershell script where you can the device the action and the initiator:


Conclusion

When tracking device wipes in Intune:

  1. Start with Tenant Audit Logs to see the action, initiator, and ObjectID.
  2. Look for "wipe ManagedDevice" in the audit logs to confirm success.
  3. Use Device Actions Report to retrieve the friendly name if the device has been deleted.
  4. Correlation ID behavior: Intune and Entra logs use separate IDs, so don’t expect a one-to-one match.

By following these steps, administrators can maintain full visibility over remote wipe actions, even when devices are removed from Intune.

Previous Post Next Post

نموذج الاتصال