Skip to main content

Bulk Autopilot Device Properties with the Graph API

The Problem
#

Windows Autopilot group tags are how you tell Autopilot which profile to use. The profile drives the entire enrollment experience – what gets installed, how the device is configured, whether it even prompts for credentials. Get the group tag wrong and you get the wrong profile, which means devices enroll wrong, which means someone re-images them manually or explains to the help desk why 50 new kiosk machines went through OOBE like standard user deployments.

The Intune portal lets you update group tags one device at a time. For 200 devices being migrated from standard to kiosk, that’s not a workflow. It’s a punishment.

The same applies to display names. Autopilot device identities ship with generic or empty display names – serial numbers at best, nothing at worst. Setting meaningful display names in bulk gives you something readable in the portal without squinting at hardware hashes.

How It Works
#

Set-AutopilotDeviceProperties.ps1 updates display names and group tags on Autopilot device identities via the updateDeviceProperties Graph API action. It supports three input modes:

# List all devices (no changes made)
.\Set-AutopilotDeviceProperties.ps1

# Export current devices to CSV (no changes made)
.\Set-AutopilotDeviceProperties.ps1 -ExportCsv "C:\Data\AutopilotDevices.csv"

# Bulk update from CSV
.\Set-AutopilotDeviceProperties.ps1 -CsvPath "C:\Data\Devices.csv"

# Single device
.\Set-AutopilotDeviceProperties.ps1 -SerialNumber "1234-5678-9012" -GroupTag "Kiosk"

# Interactive -- Out-GridView selection
.\Set-AutopilotDeviceProperties.ps1 -GroupTag "Kiosk"

# Dry run -- see what would happen
.\Set-AutopilotDeviceProperties.ps1 -CsvPath "C:\Data\Devices.csv" -WhatIf

The typical workflow: export current state with -ExportCsv, edit the CSV to set your target display names and group tags, then feed it back with -CsvPath. The export is the input template.

Prerequisites
#

  • PowerShell 5.1+ on Windows (Out-GridView in interactive mode is Windows-only)
  • Microsoft.Graph.Authentication module (Install-Module Microsoft.Graph.Authentication)
  • Permissions (delegated): DeviceManagementServiceConfig.ReadWrite.All

The script authenticates interactively via Connect-MgGraph and requests the correct scope automatically. No app registration, no secrets, no certificates.

Implementation
#

The API: updateDeviceProperties
#

The Graph API does not support PATCH on Autopilot device identities. To change a display name or group tag, you POST to the updateDeviceProperties action:

POST /deviceManagement/windowsAutopilotDeviceIdentities/{id}/updateDeviceProperties
$body = @{
    displayName = 'KIOSK-LOBBY-01'
    groupTag    = 'Kiosk'
}
Invoke-MgGraphRequest -Method POST `
    -Uri "https://graph.microsoft.com/v1.0/deviceManagement/windowsAutopilotDeviceIdentities/$id/updateDeviceProperties" `
    -Body $body

This is the only supported method. The action returns HTTP 204 and processes asynchronously – the change is accepted, not immediate. In practice, updates appear in the portal within minutes, but dynamic group recalculation can take longer (typically minutes to a couple of hours; the documented maximum is 24 hours).

CSV Format
#

Export with -ExportCsv to get a template, then edit it:

SerialNumber,DisplayName,GroupTag
1234-5678-9012,KIOSK-LOBBY-01,Kiosk
5678-9012-3456,KIOSK-LOBBY-02,Kiosk
9012-3456-7890,,Standard

SerialNumber is required. DisplayName and GroupTag are optional per row – empty values are skipped. The -DisplayName and -GroupTag parameters act as defaults when a CSV column is present but a row’s value is empty.

Per-Row Overrides
#

If the CSV includes DisplayName or GroupTag columns, the script uses per-row values where present and falls back to the parameter values where empty:

$newDN = if ($perRowDisplayName -and $row.DisplayName) { [string]$row.DisplayName }
         elseif ($DisplayName) { $DisplayName }
         else { $null }

This lets you mix per-device names with a shared group tag, or vice versa.

Idempotent Updates
#

Before calling the API, the script checks if the value already matches. If the display name or group tag is already set to the target value, the API call is skipped:

if ($entry.GroupTag -eq $device.groupTag) {
    Write-Host "  GroupTag already set: $serial -> '$($entry.GroupTag)'"
    $stats.NoChange++
    continue
}

This prevents unnecessary API calls and keeps the summary stats accurate.

API Throttling
#

The Graph API throttles Autopilot operations. The script handles HTTP 429 responses with exponential backoff – reads the Retry-After header and waits automatically, retrying up to 5 times per call. For large CSVs (hundreds of devices), expect the operation to take a few minutes as the script paces itself.

Security Considerations
#

DeviceManagementServiceConfig.ReadWrite.All grants write access to all Intune service configuration, not just Autopilot. That’s a broad scope. The script uses delegated permissions tied to your interactive sign-in, so access is bounded by your Intune RBAC role – but know what that role covers. The minimum built-in role for this operation is Intune Administrator.

Group tag changes are not cosmetic. They drive dynamic group membership, which drives Autopilot profile assignment, which drives what happens when a device enrolls. A wrong group tag on 200 devices means 200 devices enrolling with the wrong profile. Always run with -WhatIf first. There is no undo – if you set the wrong tag, you run the script again with the correct value.

The updateDeviceProperties action is asynchronous. HTTP 204 means the request was accepted, not that the change has propagated. Don’t run a second pass immediately assuming the first one failed – check the portal or re-export with -ExportCsv after a few minutes.

Changing group tags on enrolled, active devices affects live policy assignments – the dynamic group recalculates and devices can move in or out of scope for configurations they’re currently relying on. Either plan for the policy impact or run against a test batch first.

GitHub
#

Set-AutopilotDeviceProperties.ps1