Unrenamed Device Report¶
Read-only detector for Windows devices still on the default DESKTOP- name in Intune — the ones whose Autopilot rename never completed — emailed to the team with the handling rules.
Download the script View on GitHub
Read-only
It authenticates with a Managed Identity, GETs devices whose name starts with DESKTOP-, and
emails an HTML table. It needs only DeviceManagementManagedDevices.Read.All — no write scope.
The rename / reprovision steps are described in the email as a human process, not actions this
script takes.
1 · The script¶
Set the CONFIG block at the top — the SMTP server, sender, recipients and your corporate rename prefix
(all ship as @contoso.com placeholders) — then run.
View the full script
<#
.SYNOPSIS
Read-only detector for Windows devices still on the default 'DESKTOP-' name in Intune,
with an emailed HTML summary and the team's handling rules.
.DESCRIPTION
A device that enrols and keeps its out-of-the-box 'DESKTOP-XXXXX' name usually means the
corporate naming step (Autopilot rename to a standard convention) never completed. This
runbook finds those stragglers and emails a table to the support queue so they can be
followed up.
It is READ-ONLY against Microsoft Graph: it GETs managed devices and their details, then
sends an email report. It does NOT rename or wipe anything — the rename / reprovision steps
are a documented human process described in the email body, not actions this script takes.
Scope required: DeviceManagementManagedDevices.Read.All
NOTE: All names, addresses and the mail server below are SYNTHETIC examples (@contoso.com).
Replace CONFIG with your own values. Everything here is reproduced in a personal lab.
#>
# =====================================================================================
# CONFIGURE ME -> set these to your own values, then run.
# =====================================================================================
$NamePrefix = "DESKTOP-" # the default-name prefix to hunt for
$RenamePrefix = "<your-prefix>" # e.g. corporate prefix used for renamed devices
$SmtpServer = "smtp.contoso.com"
$SmtpPort = 25
$MailFrom = "servicealerts@contoso.com"
$MailTo = @("itservicedesk@contoso.com")
$SupportQueue = "your EUC Support queue" # where to route follow-up
# =====================================================================================
$ProgressPreference = 'SilentlyContinue'
$VerbosePreference = 'Continue'
Write-Verbose "Script started at $(Get-Date)"
# ---- Paged Microsoft Graph GET ----
function Invoke-MyGraphGetRequest {
param([string]$URL)
$AllResults = @()
try {
do {
$WebRequest = Invoke-WebRequest -Uri $URL -Method GET -Headers $script:Headers -UseBasicParsing
$ResponseData = ($WebRequest.Content | ConvertFrom-Json)
$AllResults += $ResponseData.value
$URL = $ResponseData.'@odata.nextLink'
} while ($URL)
return $AllResults
}
catch { Write-Error "Failed to fetch data from ${URL}: $_"; return $null }
}
# ---- Per-device detail via $select (accurate fields) ----
function Get-DeviceById {
param([Parameter(Mandatory)][string]$DeviceId)
$uri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceId`?`$select=id,deviceName,serialNumber,enrollmentProfileName,userPrincipalName,manufacturer,model,operatingSystem,lastSyncDateTime"
try { return Invoke-RestMethod -Uri $uri -Headers $script:Headers -Method Get }
catch { Write-Warning "Failed to get device details for ID ${DeviceId}: $_"; return $null }
}
# ---- Authenticate with the Automation Account Managed Identity ----
$miUrl = $env:IDENTITY_ENDPOINT
$miHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$miHeaders.Add("X-IDENTITY-HEADER", $env:IDENTITY_HEADER)
$miHeaders.Add("Metadata", "True")
$miBody = @{ resource = 'https://graph.microsoft.com/' }
$accessToken = (Invoke-RestMethod -Uri $miUrl -Method 'POST' -Headers $miHeaders -ContentType 'application/x-www-form-urlencoded' -Body $miBody).access_token
$script:Headers = @{ 'Authorization' = "Bearer $accessToken" }
Write-Verbose "Access token obtained."
# ---- Find devices still on the default name ----
$DeviceQuery = "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=startswith(deviceName,'$NamePrefix')&`$select=id"
$devicesBasicList = Invoke-MyGraphGetRequest -URL $DeviceQuery
if (-not $devicesBasicList -or $devicesBasicList.Count -eq 0) {
Write-Output "No devices with name starting '$NamePrefix' found."; return
}
Write-Verbose "Found $($devicesBasicList.Count) devices matching filter."
# ---- Enrich each with detail ----
$devicesDetailed = @()
foreach ($deviceBasic in $devicesBasicList) {
$deviceDetail = Get-DeviceById -DeviceId $deviceBasic.id
if ($deviceDetail) { $devicesDetailed += $deviceDetail }
else { Write-Warning "Skipping device id $($deviceBasic.id) due to retrieval failure." }
}
if ($devicesDetailed.Count -eq 0) { Write-Output "No detailed device information retrieved."; return }
# ---- Log the table to output too, so a read-only run can be inspected before wiring the mailer ----
$devicesDetailed |
Format-Table deviceName, serialNumber, enrollmentProfileName, userPrincipalName, model, lastSyncDateTime -AutoSize |
Out-String | Write-Output
# ---- Compose HTML rows ----
$deviceRows = ""
foreach ($dev in $devicesDetailed) {
$enc = { param($v) [System.Net.WebUtility]::HtmlEncode($v) } # works on PS 5.1 and 7 (System.Web isn't loaded on PS7)
$deviceRows += "<tr>" +
"<td>$(& $enc $dev.deviceName)</td>" +
"<td>$(& $enc $dev.serialNumber)</td>" +
"<td>$(& $enc $dev.enrollmentProfileName)</td>" +
"<td>$(& $enc $dev.userPrincipalName)</td>" +
"<td>$(& $enc $dev.manufacturer)</td>" +
"<td>$(& $enc $dev.model)</td>" +
"<td>$(& $enc $dev.operatingSystem)</td>" +
"<td>$($dev.lastSyncDateTime)</td>" +
"</tr>`n"
}
# ---- Email body (handling rules are a human process, not an action here) ----
$body = @"
<html><body>
<p>Hi Team,</p>
<p>The following devices with names starting '$NamePrefix' were detected in Intune:</p>
<table border='1' cellspacing='0' cellpadding='4' style='border-collapse:collapse;'>
<tr style='background-color:#f2f2f2;'>
<th>Device Name</th><th>Serial</th><th>Enrollment Profile</th><th>User</th>
<th>Manufacturer</th><th>Model</th><th>Operating System</th><th>Last Sync</th>
</tr>
$deviceRows
</table>
<p><b>Device handling rules:</b><br>
- If the name starts with '$NamePrefix' and the enrollment profile is present, rename to
${RenamePrefix}-<SerialNo> without a forced reboot (the rename applies on next reboot);
follow up after a few days to confirm.<br>
- If the enrollment profile is missing, the device is not Autopilot-provisioned. That is
normal for Hybrid Azure AD Join, GPO/bulk enrollment, or manual Entra join — investigate
before any reprovision; do not assume a wipe.<br>
Please route this case to $SupportQueue.<br><br>
Best regards,<br>EUC Team</p>
</body></html>
"@
# ---- Send only if devices were found ----
$subject = "Intune '$NamePrefix' devices detected - $(Get-Date -Format yyyy-MM-dd)"
# NOTE: Send-MailMessage over port 25 does NOT work from the Azure Automation sandbox
# (no outbound 25, and Exchange Online requires authenticated submission). For production,
# send via Microsoft Graph instead — grant the Mail.Send app role to this same Managed
# Identity and POST to sendMail (the $script:Headers bearer token already works):
#
# $mail = @{ message = @{
# subject = $subject
# body = @{ contentType = 'HTML'; content = $body }
# toRecipients = @($MailTo | ForEach-Object { @{ emailAddress = @{ address = $_ } } })
# } }
# Invoke-RestMethod -Method POST -Headers $script:Headers -ContentType 'application/json' `
# -Uri "https://graph.microsoft.com/v1.0/users/$MailFrom/sendMail" `
# -Body ($mail | ConvertTo-Json -Depth 6)
#
# Application Mail.Send lets the identity send as ANY mailbox — constrain it to one sender with an
# Exchange ApplicationAccessPolicy (New-ApplicationAccessPolicy) so it can't send tenant-wide.
# The Send-MailMessage call below is kept only for on-prem / authenticated-relay setups.
try {
Send-MailMessage -SmtpServer $SmtpServer -Port $SmtpPort -From $MailFrom -To $MailTo -Subject $subject -Body $body -BodyAsHtml
Write-Verbose "Email sent."
}
catch { Write-Warning "Failed to send email: $_" }
Write-Verbose "Script completed at $(Get-Date)"