AutopilotDeviceSync.ps1
<#PSScriptInfo
.VERSION 1.4 .GUID 6d57e471-104f-4165-aca1-e0c0174fd226 .AUTHOR Michael Niehaus .COMPANYNAME Microsoft .COPYRIGHT .TAGS Windows AutoPilot .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES Version 1.4: Updated authentication logic. Version 1.3: Original public version. #> <# .SYNOPSIS Verfies that all Autopilot-related devices (from the Autopilot service, Inune, and Azure AD) are all in sync, with the ability to fix them if they aren't. .DESCRIPTION This script checks all the Autopilot-related devices to make sure that they are named correctly, have the right attributes (e.g. Group Tag and Purchase Order ID), and aren't redundant (e.g. because the device has been deployed multiple times, creating a new Hybrid AADJ device each time). By default, this script will just display information about what isn't in sync. If you want it to actually fix it, you have to specify additional command line paraemters. At present, extra Hybrid Azure AD devices can be removed from Active Directory (with -CleanDevices switch) and device name issues can be fixed (with -FixNames). Due to the amount of data this will retrieve, it is possible this won't work for large tenants due to Graph API throttling. .PARAMETER CleanDevices Switch that specifies to automatically remove extra device from Active Directory. This requires that the script is running as an account that has access to Active Directory (e.g. a Domain Admin account) and on a server or workstation with the ActiveDirectory module. .PARAMETER FixNames Switch that specifies to correct any Azure AD (AzureAd) devices that have names that don't match the Intune device that it is associated with. This would typically happen in a Hybrid AADJ scenario where the device was renamed (locally and in AD) after the device was deployed. .EXAMPLE Report on any issues: .\AutopilotDeviceSync.ps1 .EXAMPLE Report on issues and fix those that can be fixed: .\AutopilotDeviceSync.ps1 -CleanDevices -FixNames #> [CmdletBinding()] param( [Parameter(Mandatory=$False)] [Switch] $CleanDevices = $false, [Parameter(Mandatory=$False)] [Switch] $FixNames = $false ) Process { Import-Module WindowsAutopilotIntune -Scope Global Import-Module AzureAD -Scope Global $intuneId = Connect-MSGraph $aadId = Connect-AzureAD -AccountId $intuneId.UPN # Data gathering $autopilotDevices = Get-AutopilotDevice | Get-MSGraphAllPages $aadDevices = Get-AzureADDevice -All $true $intuneDevices = Get-IntuneManagedDevice -Filter "contains(operatingsystem, 'Windows')" | Get-MSGraphAllPages # Maintain a list of devices to remove $extra = @() # Process each Autopilot device $autopilotDevices | % { # Find the objects linked to the Autopilot device $currentAutopilotDevice = $_ $relatedIntuneDevice = $intuneDevices | ? { $_.id -eq $currentAutopilotDevice.managedDeviceId } $relatedAadDevice = $aadDevices | ? { $_.DeviceId -eq $currentAutopilotDevice.azureActiveDirectoryDeviceId } $relatedIntuneAadDevice = $aadDevices | ? { $_.DeviceId -eq $relatedIntuneDevice.azureADDeviceId } # Find all the Azure AD devices with the ZTDID of this Autopilot device $matchedDevices = $aadDevices | ? { $_.DevicePhysicalIds -match $currentAutopilotDevice.Id } # Display a summary for this device Write-Host "$($currentAutopilotDevice.SerialNumber):" Write-Host " Au:AAD = $($relatedAadDevice.DisplayName) $($relatedAadDevice.DeviceTrustType) Au:Intune = $($relatedIntuneDevice.DeviceName) Intune:AAD = $($relatedIntuneAadDevice.DisplayName) $($relatedIntuneAadDevice.DeviceTrustType)" # If there are no devices in AAD with this ZTDID, the pre-created device was removed - that's bad if ($matchedDevices.Count -eq 0) { Write-Host " No AAD devices found with the ZTDID" -ForegroundColor Red } else { # We would normally expect one AAD device for AAD Join scenarios, and two for Hybrid AAD Join scenarios, but there can be more Write-Host " $($matchedDevices.Count) devices found with the ZTDID" # Check if Intune and Autopilot are linked to the same AAD device. With Hybrid AADJ, they are typically different. if ($relatedIntuneDevice) { if ($relatedIntuneDevice.azureADDeviceId -ne $relatedAadDevice.DeviceId) { Write-Host " Intune and Autopilot are linked to different AAD devices" } elseif ($relatedIntuneDevice.DeviceName -ne $relatedAadDevice.DisplayName) { # This can be fixed later Write-Host " Intune and AAD device names do not match" -ForegroundColor Yellow } } $matchedDevices | % { # Make sure the ZTDID-matched AAD device ($_) is the one associated with the Intune object if ($relatedIntuneDevice -and ($_.DeviceId -eq $relatedIntuneDevice.azureADDeviceId)) { Write-Host " AAD:Intune object match $($_.DisplayName) $($_.DeviceTrustType)" } # Make sure the ZTDID-matched AAD device ($_) is the one associated with the Autopilot object elseif ($_.DeviceId -eq $currentAutopilotDevice.azureActiveDirectoryDeviceId) { Write-Host " AAD:Au object match $($_.DisplayName) $($_.DeviceTrustType)" } # Otherwise, this is an extra object else { if ($relatedIntuneDevice -and ($relatedIntuneDevice.DeviceName -eq $_.DisplayName)) { Write-Host " Found match on Intune device name, not safe to remove" } elseif ($_.DeviceTrustType -eq 'ServerAd') { Write-Host " Extra Hybrid AADJ device $($_.DisplayName) $($_.DeviceTrustType)" -ForegroundColor Yellow $extra += $_ } else { Write-Host " Extra AAD device $($_.DisplayName) $($_.DeviceTrustType), manually remove" -ForegroundColor Yellow } } # Check the device name (assuming the Intune device has the right name, which is typically the case) if ($_.DeviceTrustType -eq 'AzureAd') { if ($relatedIntuneDevice) { if ($relatedIntuneDevice.deviceName -ne $_.DisplayName) { Write-Host " AAD name mismatch $($relatedIntuneDevice.deviceName) $($_.DisplayName)" -ForegroundColor Yellow if ($FixNames) { Set-AzureADDevice -ObjectId $_.ObjectId -DisplayName $relatedIntuneDevice.deviceName } } } } # Check if the device has the expected attributes, if not already marked for removal if (-not ($extra -contains $_)) { if ($currentAutopilotDevice.groupTag) { if (-not ($_.DevicePhysicalIds -match $currentAutopilotDevice.groupTag)) { Write-Host " GroupTag missing from AAD device $($_.DisplayName) $($_.DeviceTrustType)" -ForegroundColor Yellow } } if ($currentAutopilotDevice.purchaseOrderIdentifier) { if (-not ($_.DevicePhysicalIds -match $currentAutopilotDevice.purchaseOrderIdentifier)) { Write-Host " PurchaseOrderId missing from AAD device $($_.DisplayName) $($_.DeviceTrustType)" -ForegroundColor Yellow } } } } } } # Remove extra devices Write-Host " " if ($CleanDevices) { Write-Host "Removing unused (ZTDID-matched) Hybrid Azure AD Join devices from Active Directory:" $extra | % { Write-Host " $($_.DisplayName)" # First try to remove it from AD so the deletion can sync to AAD $current = $_ try { Get-ADComputer -Identity $current.DisplayName | Remove-ADObject -Recursive -Confirm:$false } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { # If the deletion failed from AD because it wasn't found, just remove it from AAD directly Remove-AzureADDevice -ObjectId $current.ObjectId } } } else { Write-Host "Hybrid Azure AD Join (ServerAd) devices that can be removed from Active Directory:" $extra | % { # Make sure we can retrieve the AD device object $_.DisplayName try { $null = Get-ADComputer -Identity $_.DisplayName } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { # This error is OK } } } } |