Now booking Q1 Intune migrations โ€” talk to an engineer.

CyberSystem
โ† Back to Blog

Dynamic Device Renaming in Intune Using Group Tags and PowerShell

Automate device renaming in Microsoft Intune using Group Tags and PowerShell, avoiding the 350 Autopilot profile limit while maintaining clean, readable device names that comply with NetBIOS naming constraints.

By Ali Alameโ€ข
intunepowershelldevice-managementautomationmicrosoft-365autopilotgraph-api

Managing device names in Microsoft Intune at scale can become a messy, manual burden, especially when you have multiple naming standards across regions, departments, or user types.

While Intune allows you to use Autopilot deployment profiles to assign naming conventions, this model breaks down fast:

๐Ÿ˜ฉ The Real-World Challenges

  • Intune device names are capped at 15 characters - This is a NetBIOS naming limitation that affects all Windows devices.

  • You're limited to 350 Autopilot profiles - As documented in Windows Autopilot deployment limits, this constraint makes it impossible to create a profile for every naming scheme.

  • Device names default to SerialNumber or Random - Without proper configuration, devices end up with unreadable names that make organization difficult.

  • Creating a profile for every naming scheme is neither efficient, feasible, nor sustainable - This approach doesn't scale with organizational growth.

๐ŸŽฏ The Smarter Solution

Instead of tying your naming conventions to deployment profiles, use Group Tags to define personas or functions and let a PowerShell script handle the renaming post-deployment.

By leveraging Intune's dynamic group tagging and Microsoft Graph API, you can:

  • Apply only a few general deployment profiles
  • Automatically rename devices based on their Group Tag + a shortened serial tail
  • Stay well within Intune limits while keeping device names clean and meaningful

๐Ÿ” Prerequisites

Step 1: Create an Azure App Registration

  1. Go to Azure Portal โ†’ Azure Active Directory โ†’ App registrations.
  2. Click New registration.
  3. Name it something like DeviceRenamer.
  4. For detailed guidance, see Register an application with the Microsoft identity platform.

Step 2: Assign API Permissions

Under API permissions, add the following Microsoft Graph permissions:

  • Device.Read.All (Application) - Required to read device information
  • DeviceManagementServiceConfig.Read.All (Application) - Required for Autopilot device queries
  • User.Read (Delegated) - Optional, for user context if needed

Important: Grant admin consent for these permissions. Learn more about permission types and consent.

Step 3: Generate Client Credentials

  1. Under Certificates & secrets:
  2. Click New client secret
  3. Store the Client ID, Tenant ID, and Secret securely

For more information on app authentication, see Microsoft identity platform application authentication.

๐Ÿง  Script Overview: What It Does

Let's break down the core logic of the script:

๐Ÿ” 1. Authentication

The script authenticates to Microsoft Graph using the Azure App credentials via the OAuth 2.0 client credentials flow:

$TenantId     = '...'
$ClientId     = '...'
$ClientSecret = '...'

It requests an access token to call Graph API endpoints securely.

๐Ÿ” 2. Get Serial Number

It retrieves the local BIOS serial number:

$Serial = (Get-CimInstance Win32_BIOS).SerialNumber.Trim()

This is used to match the device record in Graph. Learn more about querying device information with WMI.

๐Ÿ“ก 3. Query Device Metadata

Using the Microsoft Graph API, the script pulls the device's full Autopilot profile, including its Group Tag:

$Device = $AllDevices.value | Where-Object { $_.serialNumber -eq $Serial }
$GroupTag = $Device.groupTag

The script queries the Windows Autopilot device identities endpoint.

๐Ÿงฎ 4. Calculate the New Name

The script constructs a new name that fits Intune's 15-character limit:

$BaseLen = $GroupTag.Length + 1  # +1 for hyphen
$AvailLen = 15 - $BaseLen
$SerialTail = if ($SerialClean.Length -le $AvailLen) { 
    $SerialClean 
} else { 
    $SerialClean.Substring($SerialClean.Length - $AvailLen, $AvailLen) 
}
$NewName = "$GroupTag-$SerialTail"

So if the Group Tag is 6 characters, the last 8 characters of the serial will be used. It dynamically adjusts.

โœ… This guarantees:

๐Ÿ–ฅ๏ธ 5. Rename the Device

If the device isn't already named correctly, it triggers the rename using PowerShell's Rename-Computer cmdlet:

Rename-Computer -NewName $NewName -Force

It also logs success or failure and schedules a reboot.

๐Ÿ“ Logging

All operations are logged to:

C:\ProgramData\IntuneDeviceRenamer\logs\

This helps with troubleshooting and auditability. For best practices on PowerShell logging, see PowerShell logging and auditing.

๐Ÿš€ Deployment Strategy

Here's how you can use this at scale:

  1. Assign Group Tags to Autopilot devices in Intune (e.g., FINANCE, UK-HR, NYC-SALES). Learn how in Add Group Tags to Autopilot devices.

  2. Use just a few deployment profiles (e.g., by region or OS version). See Create and assign Autopilot deployment profiles.

  3. Run this script during the provisioning process or as a scheduled task post-enrollment. For deploying PowerShell scripts via Intune, see Add PowerShell scripts to Microsoft Intune.

The device auto-renames based on its tag and serial tail โ€” clean, readable, and dynamic.

๐Ÿ“š Additional Resources

๐Ÿ”š Final Thoughts

This approach offers the best of both worlds:

โœ… Clean, human-readable names
โœ… Dynamic device group alignment
โœ… Minimal deployment profiles
โœ… Fully automated and scalable

You don't have to choose between flexibility and manageability โ€” you can have both.


Full PowerShell Script

<#
.Author
    AliAlame - CYBERSYSTEM

.SYNOPSIS
    Renames an AAD-joined Intune device to "<OrderID>-<SerialTail>" (โ‰ค15 chars)
    โ€”with verbose console output for troubleshooting.

.NOTES
    โ€ข Requires Graph application permission Device.Read.All (app registration)
    โ€ข Fill in $TenantId $ClientId $ClientSecret below
    โ€ข Logs + console: C:\ProgramData\IntuneDeviceRenamer\logs\
#>

#Device.Read.All (Application)
#DeviceManagementServiceConfig.Read.All (Application)
#User.Read (Delegated)

# ========= 0.  SETTINGS =========

$TenantId     = 'XXXXXXXXXXXXXXXXXXXXXXX'
$ClientId     = 'XXXXXXXXXXXXXXXXXXXXXXX'
$ClientSecret = 'XXXXXXXXXXXXXXXXXXXXXXX'
$DebugMode    = $false          # true = no rename, no reboot, prints extra

# === 1. Logging ===

$LogDir = 'C:\ProgramData\IntuneDeviceRenamer\logs'
if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }

$LogFile = Join-Path $LogDir ("Rename_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date))

function Log { param($m, $l='INFO'); ("{0:o} [{1}] {2}" -f (Get-Date), $l, $m) | Tee-Object -FilePath $LogFile -Append }

Log "=== Rename-From-GroupTag START ==="

# === 2. BIOS Serial (RAW) ===

$Serial = (Get-CimInstance Win32_BIOS).SerialNumber.Trim()

if (-not $Serial) { Log 'ERR: BIOS serial empty.' 'ERROR'; exit 1 }

Log "Serial (raw) = $Serial"

# === 3. Graph Token ===

try {
    $Body = "client_id=$ClientId" +
            "&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default" +
            "&client_secret=$([uri]::EscapeDataString($ClientSecret))" +
            "&grant_type=client_credentials"

    $AccessToken = (Invoke-RestMethod -Method POST `
        -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" `
        -Body $Body -ContentType 'application/x-www-form-urlencoded' `
        -Verbose:$DebugMode).access_token

    Log "Token OK (len=$($AccessToken.Length))"
} catch {
    Log "ERR: Token request failed. $_" 'ERROR'; exit 1
}

# === 4. Query Autopilot Devices (NO $filter) ===

$Uri = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities"

try {
    $AllDevices = Invoke-RestMethod -Uri $Uri -Headers @{Authorization="Bearer $AccessToken"} -Method GET -Verbose:$DebugMode

    $Device = $AllDevices.value | Where-Object { $_.serialNumber -eq $Serial }

    if (-not $Device) { Log "ERR: No Autopilot record found for serial $Serial" 'ERROR'; exit 1 }

    $GroupTag = $Device.groupTag

    Log "Found GroupTag = $GroupTag"
} catch {
    Log "ERR: Graph query failed. $_" 'ERROR'; exit 1
}

if (-not $GroupTag) { Log "ERR: GroupTag is empty." 'ERROR'; exit 1 }

# === 5. Build New Name ===

# Clean serial for naming
$SerialClean = ($Serial -replace '[^0-9A-Za-z]', '')

Log "Serial (cleaned) = $SerialClean"

$MaxLen   = 15
$BaseLen  = $GroupTag.Length + 1  # +1 for hyphen
$AvailLen = $MaxLen - $BaseLen

if ($AvailLen -le 0) { Log "ERR: GroupTag too long for NetBIOS limit." 'ERROR'; exit 1 }

# Use cleaned serial for SerialTail
$SerialTail = if ($SerialClean.Length -le $AvailLen) { $SerialClean }
              else { $SerialClean.Substring($SerialClean.Length - $AvailLen, $AvailLen) }

$NewName = "$GroupTag-$SerialTail"

Log "Proposed new name = $NewName"

# === 6. Rename Computer ===

if ($env:COMPUTERNAME -ieq $NewName) {
    Log "Already correctly named. EXIT."
    exit 0
}

try {
    Rename-Computer -NewName $NewName -Force -ErrorAction Stop
    Log "Rename-Computer succeeded."
} catch {
    Log "ERR: Rename-Computer failed. $_" 'ERROR'; exit 1
}

# === 7. Handle Reboot ===

$Cs = Get-CimInstance Win32_ComputerSystem

if ($Cs.UserName -match 'defaultUser') {
    Log "In ESP/OOBE โ€” Exiting 1641 for forced reboot"
    exit 1641
} else {
    try {
        Add-Type -AssemblyName PresentationFramework
        [System.Windows.MessageBox]::Show(
            "Device name was updated to:`n$NewName`n`nThe system will reboot automatically in 10 minutes, or you can reboot manually now.",
            "Device Renamed",
            "OK",
            "Info"
        ) | Out-Null
        shutdown.exe /g /t 600 /f /c "Restarting after device rename to $NewName."
    } catch {
        Log "Fallback: shutdown command triggered."
        shutdown.exe /g /t 600 /f /c "Restarting after device rename to $NewName."
    }
    exit 0
}