Fix for incorrectly named devices enrolled using Autopilot through Azure Automation
How to make sure that AAD-joined Autopilot devices will have the correct hostname?
We are using Device name
attribute of the Autopilot device record for setting the device hostname during Autopilot enrollment (OOBE).
It's convenient and can be used as the single source of truth because this value cannot be changed by your users, unlike the device's real hostname.
This solution has one caveat though and that's Autopilot's well-known unreliability. That can lead to devices with the wrong (a.k.a. default 'DESK-%RAND%') hostname instead of the one you've defined.
This typically happens when Autopilot enrollment is run on a Wi-Fi network or using an external USB ethernet dongle. So in general it is caused by network latency that results in the client not downloading the correct policies from Intune before enrollment continues.
Solution
In general, you can solve this issue with wrongly named clients using Remediation script
which would change the device hostname using some hardcoded data (for example CSV with device serial number and corresponding hostname).
But this post is about a more robust, set-once-and-forget solution.
We will use Azure Automation Runbook
that will:
- on schedule cycle through all Intune Windows devices
- for each device finds corresponding Autopilot record using its serial number
- if current name doesn't match the Autopilot ones, invoke Intune Rename functionality
Some of the code and idea of using Intune built-in rename functionality is based on this blog post ๐.
Requirements
- permissions to create Azure Automation Account
- permissions to grant permission to Azure Service principal
How to
Create Azure Automation Account
- Log in to your
Azure portal
- Create new
Automation Account
- my account will be named device-monitoring
- Scroll down to
Identity
section and createSystem assigned
managed identity- make a note of this newly created service principal ID as we are going to use it for granting permissions
Grant Graph API permissions to the created Automation Account
- Use PowerShell code below to grant required permissions
$servicePrincipalId = '<setThisToManagedIdentityID>'
$resourceAppId = '00000003-0000-0000-c000-000000000000' # graph api
$permissionList = 'Device.ReadWrite.All', 'DeviceManagementServiceConfig.Read.All', 'DeviceManagementManagedDevices.ReadWrite.All', 'DeviceManagementManagedDevices.PrivilegedOperations.All'
$servicePrincipal = (Get-AzureADServicePrincipal -ObjectId $servicePrincipalId)
if (!$servicePrincipal) { throw "Service principal '$servicePrincipalId' doesn't exist" }
# get application whose permissions will be granted
$resourceServicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '$resourceAppId'"
if (!$resourceServicePrincipal) { throw "Resource '$resourceAppId' doesn't exist" }
# grant requested permissions
foreach ($permission in $permissionList) {
$AppRole = $resourceServicePrincipal.AppRoles | Where-Object { $_.Value -eq $permission -and $_.AllowedMemberTypes -contains "Application" }
if (!$AppRole) {
Write-Warning "Application permission '$permission' wasn't found in '$resourceAppId' application. Therefore it cannot be added."
continue
}
New-AzureADServiceAppRoleAssignment -ObjectId $servicePrincipal.ObjectId -PrincipalId $servicePrincipal.ObjectId -ResourceId $resourceServicePrincipal.ObjectId -Id $AppRole.Id
}
And the result in Azure Portal should look like this
Create a new Runbook
- Create a new Runbook
- Paste following PowerShell code using
edit
button
# get authentication token
function Get-AuthToken {
try {
# obtain AccessToken for Microsoft Graph via the managed identity
$ResourceURL = "https://graph.microsoft.com"
$Response = [System.Text.Encoding]::Default.GetString((Invoke-WebRequest -UseBasicParsing -Uri "$($env:IDENTITY_ENDPOINT)?resource=$resourceURL" -Method 'GET' -Headers @{'X-IDENTITY-HEADER' = "$env:IDENTITY_HEADER"; 'Metadata' = 'True' }).RawContentStream.ToArray()) | ConvertFrom-Json
# construct AuthHeader
$AuthHeader = @{
'Content-Type' = 'application/json'
'Authorization' = "Bearer " + $Response.access_token
}
} catch {
throw $_
}
return $authHeader
}
$header = Get-AuthToken
$managedDeviceList = $null
$uri = 'https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$filter=startswith(operatingSystem,''Windows'')'
$managedDeviceList = (Invoke-RestMethod -Uri $uri -Headers $header -ErrorAction Stop).value
$autopilotDeviceList = $null
$uri = 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities' #?$select=displayName,serialNumber
$autopilotDeviceList = (Invoke-RestMethod -Uri $uri -Headers $header -ErrorAction Stop).value
foreach ($device in $managedDeviceList) {
$currentDeviceName = $device.deviceName
$deviceID = $device.id
$deviceSerial = $device.serialNumber
if ($device.model -eq "Virtual Machine") {
"Device $currentDeviceName is a VM. Skipping"
continue
}
$correctDeviceName = $autopilotDeviceList | ? serialNumber -eq $deviceSerial | select -expandProperty displayName
if (!$correctDeviceName) {
Write-Error "Unable to get displayName of the $currentDeviceName ($deviceSerial) from the Autopilot database"
continue
}
# Virtual computers have the text "SerialNumber" as serialnumber...
if ($currentDeviceName -ne $correctDeviceName) {
$uri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$deviceID/setDeviceName"
$JSONPayload = @{
"deviceName" = $correctDeviceName
}
$convertedJSONPayLoad = $JSONPayload | ConvertTo-Json
Write-Warning "Renaming $currentDeviceName to $correctDeviceName"
$null = Invoke-RestMethod -Uri $URI -Method POST -Body $convertedJSONPayLoad -Headers $header -Verbose -ErrorAction Stop
}
}
Save & publish it.
Create schedule and link it to your Runbook
Thats it ๐
You can test your new Runbook using Start
button in the top menu.