Powershell : Intune Wipe/Reset Reporting

This is a post that got me curious as to whether it could be done, well using Google to check his facts I came across this glimmer of light that says yes, it can be tracked:

View the Audit logs: You can see who started a Retire/Wipe by going to Tenant administration > Audit logs and checking the Initiated By column. If there's no entry, the device's user initiated the action

This is absolutely fantastic news because if it’s in the audit log, it can also be extracted through a power shelf script that uses API calls connect connecting to Graph.

This is good news because that means you can track these actions and report on them not only for mobile devices, but also Windows laptops at the same time, lets get coding.

Pre-requisites Requirements

You need to ensure you have the Graph API installed and registered for this to work, this can be completed with the command:

Install-Module Microsoft.Graph -Force -AllowClobber
Import-Module Microsoft.Graph

This should download and install all the modules required for this command to work effectively

Script : WipeReport.ps1

# App registration details
$tenantId = "<tenant_id>"
$clientId = "<client_id>"
$clientSecret = "<secret>"

# Get token
$body = @{
    Grant_Type = "client_credentials"
    Scope = "https://graph.microsoft.com/.default"
    Client_Id = $clientId
    Client_Secret = $clientSecret
}

Write-Verbose "Getting access token..." -Verbose
$response = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method POST -Body $body
$token = $response.access_token

#Write-Verbose "Connecting to Microsoft Graph..." -Verbose
#Connect-MgGraph -AccessToken $token

# Convert token to SecureString
$secureToken = ConvertTo-SecureString $token -AsPlainText -Force

# Connect using SecureString token
Connect-MgGraph -AccessToken $secureToken

# Get the date from 30 days ago (maximum audit log retention)
$startDate = (Get-Date).AddDays(-30).ToString('yyyy-MM-dd')
$endDate = (Get-Date).ToString('yyyy-MM-dd')
Write-Verbose "Checking audit logs from $startDate to $endDate" -Verbose

# Initialize arrays for different device types
$mobileWipeReport = @()
$windowsWipeReport = @()
Write-Verbose "Fetching audit logs for wipe and reset commands..." -Verbose
$auditLogs = Get-MgAuditLogDirectoryAudit -Filter "activityDateTime ge $startDate and (activityDisplayName eq 'Wipe device' or activityDisplayName eq 'Reset device')" -Verbose
Write-Verbose "Found $($auditLogs.Count) audit log entries" -Verbose
foreach ($log in $auditLogs) {
    Write-Verbose "Processing audit log entry from $($log.ActivityDateTime)" -Verbose

  # Get device details
    $deviceId = $log.TargetResources.Id
    Write-Verbose "Getting device details for ID: $deviceId" -Verbose
    $device = Get-MgDeviceManagementManagedDevice -ManagedDeviceId $deviceId -ErrorAction SilentlyContinue
        # Skip if device not found
    if (-not $device) { 
        Write-Verbose "Device not found, skipping..." -Verbose
        continue 
    }
       Write-Verbose "Getting user details for device owner and initiator..." -Verbose
    # Get user who owned the device
    $deviceUser = Get-MgUser -UserId $device.UserId -ErrorAction SilentlyContinue
        # Get admin who initiated wipe
    $initiatedBy = Get-MgUser -UserId $log.InitiatedBy.User.Id -ErrorAction SilentlyContinue
     Write-Verbose "Creating report entry for device: $($device.DeviceName)" -Verbose
    # Create report object
    $reportEntry = [PSCustomObject]@{
        WipeDate = $log.ActivityDateTime
        DeviceName = $device.DeviceName       
        DeviceModel = $device.Model
        DeviceOS = $device.OperatingSystem
        OSVersion = $device.OsVersion
        DeviceOwner = if ($deviceUser) { "$($deviceUser.DisplayName) ($($deviceUser.UserPrincipalName))" } else { "Unknown" }
        WipeInitiatedBy = if ($initiatedBy) { "$($initiatedBy.DisplayName) ($($initiatedBy.UserPrincipalName))" } else { "Unknown" }
        ActionType = $log.ActivityDisplayName
        Status = $device.DeviceActionStatus
        SerialNumber = $device.SerialNumber
        LastSyncDateTime = $device.LastSyncDateTime
    }   

    # Sort into appropriate report based on OS
    if ($device.OperatingSystem -match 'Windows') {
        Write-Verbose "Adding to Windows report" -Verbose
        $windowsWipeReport += $reportEntry
    } else {
        Write-Verbose "Adding to Mobile report" -Verbose
        $mobileWipeReport += $reportEntry
    }
}

Write-Verbose "Preparing to export reports..." -Verbose
# Export to separate CSV files
$dateStamp = Get-Date -Format 'yyyyMMdd'
$mobileExportPath = "Intune_Mobile_Wipe_Report_$dateStamp.csv"
$windowsExportPath = "Intune_Windows_Wipe_Report_$dateStamp.csv"
Write-Verbose "Exporting mobile device report to $mobileExportPath" -Verbose
$mobileWipeReport | Export-Csv -Path $mobileExportPath -NoTypeInformation
Write-Verbose "Exporting windows device report to $windowsExportPath" -Verbose
$windowsWipeReport | Export-Csv -Path $windowsExportPath -NoTypeInformation
# Display summary on screen
Write-Host "`nMobile Device Wipes:"
Write-Host "Total wipes found: $($mobileWipeReport.Count)"
Write-Host "Report exported to: $mobileExportPath"
$mobileWipeReport | Format-Table -AutoSize
Write-Host "`nWindows Device Wipes/Resets:"
Write-Host "Total wipes/resets found: $($windowsWipeReport.Count)"
Write-Host "Report exported to: $windowsExportPath"
$windowsWipeReport | Format-Table -AutoSize
Write-Verbose "Script completed successfully" -Verbose

The script should run like this, you can see here that zero wipes have been detected for laptops and mobiles:


This will produce the file you need for the report to be run, which in this example is run separately with another script, so if you have data to add to the report then you can use the script below to produce a nice HTML report that is mobile friendly.

Script : Reporter.ps1

# Get latest CSV files
$mobileFile = Get-ChildItem -Path . -Filter "*mobile*.csv" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$windowsFile = Get-ChildItem -Path . -Filter "*windows*.csv" | Sort-Object LastWriteTime -Descending | Select-Object -First 1

$mobileWipes = Import-Csv $mobileFile.FullName
$windowsWipes = Import-Csv $windowsFile.FullName

$html = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Device Wipe Dashboard</title>
    <style>
        :root {
            --primary-color: #2c3e50;
            --secondary-color: #34495e;
            --accent-color: #3498db;
            --text-color: #2c3e50;
            --background-color: #ecf0f1;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            line-height: 1.6;
            color: var(--text-color);
            background: var(--background-color);
            padding: 2rem;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 2rem;
            background: white;
            border-radius: 1rem;
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
        }

        h1 {
            text-align: center;
            color: var(--primary-color);
            margin-bottom: 2rem;
            font-size: 2.5rem;
        }

        .dashboard {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 2rem;
            margin-top: 2rem;
        }

        .section {
            background: white;
            border-radius: 0.5rem;
            overflow: hidden;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            transition: transform 0.3s ease;
        }

        .section:hover {
            transform: translateY(-5px);
        }

        .section-header {
            background: var(--primary-color);
            color: white;
            padding: 1rem;
            font-size: 1.25rem;
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .section-header::before {
            content: '';
            display: inline-block;
            width: 24px;
            height: 24px;
            background-size: contain;
            background-repeat: no-repeat;
        }

        .mobile-header::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='white' viewBox='0 0 24 24'%3E%3Cpath d='M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z'/%3E%3C/svg%3E");
        }

        .laptop-header::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='white' viewBox='0 0 24 24'%3E%3Cpath d='M20 18c1.1 0 1.99-.9 1.99-2L22 5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2H0c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2h-4zM4 5h16v11H4V5z'/%3E%3C/svg%3E");
        }

        table {
            width: 100%;
            border-collapse: collapse;
        }

        th, td {
            padding: 1rem;
            text-align: left;
            border-bottom: 1px solid #eee;
        }

        th {
            background: var(--secondary-color);
            color: white;
            font-weight: 500;
        }

        tr:nth-child(even) {
            background: #f8f9fa;
        }

        tr:hover {
            background: #e9ecef;
        }

        @media (max-width: 768px) {
            .dashboard {
                grid-template-columns: 1fr;
            }

            th, td {
                padding: 0.5rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Device Wipe Dashboard</h1>
        <div class="dashboard">
            <div class="section">
                <div class="section-header mobile-header">Mobile Devices</div>
                <table>
                    <thead>
                        <tr>
                            <th>Device</th>
                            <th>Name</th>
                            <th>Initiated By</th>
                        </tr>
                    </thead>
                    <tbody>
                        $(foreach ($device in $mobileWipes) {
                            "<tr>
                                <td>$($device.DeviceModel)</td>
                                <td>$($device.DeviceName)</td>
                                <td>$($device.WipeInitiatedBy)</td>
                            </tr>"
                        })
                    </tbody>
                </table>
            </div>
            <div class="section">
                <div class="section-header laptop-header">Laptops</div>
                <table>
                    <thead>
                        <tr>
                            <th>Device</th>
                            <th>Name</th>
                            <th>Initiated By</th>
                        </tr>
                    </thead>
                    <tbody>
                        $(foreach ($device in $windowsWipes) {
                            "<tr>
                                <td>$($device.DeviceModel)</td>
                                <td>$($device.DeviceName)</td>
                                <td>$($device.WipeInitiatedBy)</td>
                            </tr>"
                        })
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</body>
</html>
"@

$html | Out-File "DeviceWipeReport.html" -Encoding UTF8

When this reporter creation is run you should then get an output file in graphical format as below, obviously this is "made up" data:


Trouble running the script?

If when you try to run the script you get a sea of red errors then you may need to refresh your Graph permissions and modules, to get this to work you need to follow these instructions:

  1. Close ALL PowerShell windows and applications using PowerShell
  2. Open a new PowerShell window as Administrator
  3. Run the script below
Script : Remove-Modules.ps1

# First remove modules from current session
Get-Module Microsoft.Graph* | Remove-Module -Force

# Now try to uninstall all Graph modules
$modules = Get-Module -Name Microsoft.Graph* -ListAvailable | Select-Object -ExpandProperty Name -Unique
foreach ($module in $modules) {
    Write-Host "Removing module: $module"
    try {
        Uninstall-Module -Name $module -Force -AllVersions -ErrorAction Continue
    }
    catch {
        Write-Warning "Could not remove $module : $_"
    }
}

Once this script has completed you can then run this command to reinstall Graph and import it:

Install-Module Microsoft.Graph -Force -AllowClobber
Import-Module Microsoft.Graph

Previous Post Next Post

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