Test-AutopilotAzureADDeviceAssociation.ps1

<#PSScriptInfo
.VERSION 1.0.0
.GUID c30dcb72-e391-49ae-ad06-e5438b8c72a1
.AUTHOR NickolajA
.DESCRIPTION Validate that the configured Azure AD device record for all Autopilot device identities exist in Azure AD.
.COMPANYNAME MSEndpointMgr
.COPYRIGHT
.TAGS AzureAD Autopilot Windows Intune
.LICENSEURI
.PROJECTURI https://github.com/MSEndpointMgr/Intune/blob/master/Autopilot/Test-AutopilotAzureADDeviceAssociation.ps1
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
#>

#Requires -Module MSGraphRequest
#Requires -Module MSAL.PS
<#
.SYNOPSIS
    Validate that the configured Azure AD device record for all Autopilot device identities exist in Azure AD.
 
.DESCRIPTION
    This script will retrieve all Autopilot identities and foreach validate if the given Azure AD device record that's
    currently associated actually exist in Azure AD.
 
.PARAMETER TenantID
    Specify the tenant name or ID, e.g. tenant.onmicrosoft.com or <GUID>.
 
.EXAMPLE
    .\Test-AutopilotAzureADDeviceAssociation.ps1 -TenantID "tenantname.onmicrosoft.com"
 
.NOTES
    FileName: Test-AutopilotAzureADDeviceAssociation.ps1
    Author: Nickolaj Andersen
    Contact: @NickolajA
    Created: 2021-05-06
    Updated: 2021-05-06
 
    Version history:
    1.0.0 - (2021-05-06) Script created
#>

[CmdletBinding(SupportsShouldProcess = $true)]
param(
    [parameter(Mandatory = $true, HelpMessage = "Specify the tenant name or ID, e.g. tenant.onmicrosoft.com or <GUID>.")]
    [ValidateNotNullOrEmpty()]
    [string]$TenantID
)
Process {
    # Functions
    function Get-AutopilotDevice {
        <#
        .SYNOPSIS
            Retrieve all Autopilot device identities.
             
        .DESCRIPTION
            Retrieve all Autopilot device identities.
             
        .NOTES
            Author: Nickolaj Andersen
            Contact: @NickolajA
            Created: 2021-01-27
            Updated: 2021-01-27
 
            Version history:
            1.0.0 - (2021-01-27) Function created
        #>
    
        Process {
            # Retrieve all Windows Autopilot device identities
            $ResourceURI = "deviceManagement/windowsAutopilotDeviceIdentities"
            $GraphResponse = Invoke-MSGraphOperation -Get -APIVersion "Beta" -Resource $ResourceURI

            # Handle return response
            return $GraphResponse
        }
    }

    function Get-AzureADDeviceRecord {
        <#
        .SYNOPSIS
            Retrieve an Azure AD device record.
             
        .DESCRIPTION
            Retrieve an Azure AD device record.
 
        .PARAMETER DeviceId
            Specify the Device ID of the Azure AD device record.
             
        .NOTES
            Author: Nickolaj Andersen
            Contact: @NickolajA
            Created: 2021-05-05
            Updated: 2021-05-05
 
            Version history:
            1.0.0 - (2021-05-05) Function created
        #>
 
        param(
            [parameter(Mandatory = $true, HelpMessage = "Specify the Device ID of the Azure AD device record.")]
            [ValidateNotNullOrEmpty()]
            [string]$DeviceId
        )   
        Process {
            # Retrieve all Windows Autopilot device identities
            $ResourceURI = "devices?`$filter=deviceId eq '$($DeviceId)'"
            $GraphResponse = (Invoke-MSGraphOperation -Get -APIVersion "v1.0" -Resource $ResourceURI).value

            # Handle return response
            return $GraphResponse
        }
    }

    # Get access token
    $AccessToken = Get-AccessToken -TenantID $TenantID

    # Construct array list for all Autopilot device identities with broken associations
    $AutopilotDeviceList = New-Object -TypeName "System.Collections.ArrayList"

    # Gather Autopilot device details
    Write-Verbose -Message "Attempting to retrieve all Autopilot device identities, this could take some time"
    $AutopilotDevices = Get-AutopilotDevice

    # Measure detected Autopilot identities count
    $AutopilotIdentitiesCount = ($AutopilotDevices | Measure-Object).Count

    if ($AutopilotDevices -ne $null) {
        Write-Verbose -Message "Detected count of Autopilot identities: $($AutopilotIdentitiesCount)"

        # Construct and start a timer for output
        $Timer = [System.Diagnostics.Stopwatch]::StartNew()
        $AutopilotIdentitiesCurrentCount = 0
        $SecondsCount = 0

        # Process each Autopilot device identity
        foreach ($AutopilotDevice in $AutopilotDevices) {
            # Increase current progress count
            $AutopilotIdentitiesCurrentCount++

            # Handle output count for progress visibility
            if ([math]::Round($Timer.Elapsed.TotalSeconds) -gt ($SecondsCount + 30)) {
                # Increase minutes count for next output frequence
                $SecondsCount = [math]::Round($Timer.Elapsed.TotalSeconds)

                # Write output every 30 seconds
                Write-Verbose -Message "Elapsed time: $($Timer.Elapsed.Hours) hour $($Timer.Elapsed.Minutes) min $($Timer.Elapsed.Seconds) seconds"
                Write-Verbose -Message "Progress count: $($AutopilotIdentitiesCurrentCount) / $($AutopilotIdentitiesCount)"
                Write-Verbose -Message "Detected devices: $($AutopilotDeviceList.Count)"
            }

            # Handle access token refresh if required
            $AccessTokenRenew = Test-AccessToken
            if ($AccessTokenRenew -eq $false) {
                $AccessToken = Get-AccessToken -TenantID $TenantID -Refresh
            }

            # Get Azure AD device record for associated device based on what's set for the Autopilot identity
            $AzureADDevice = Get-AzureADDeviceRecord -DeviceId $AutopilotDevice.azureAdDeviceId
            if ($AzureADDevice -eq $null) {
                # Construct custom object for output
                $PSObject = [PSCustomObject]@{
                    Id = $AutopilotDevice.id
                    SerialNumber = $AutopilotDevice.serialNumber
                    Model = $AutopilotDevice.model
                    Manufacturer = $AutopilotDevice.manufacturer
                }
                $AutopilotDeviceList.Add($PSObject) | Out-Null
            }
        }

        # Handle output at script completion
        Write-Verbose -Message "Successfully detected a total of '$($AutopilotDeviceList.Count)' Autopilot identities with a broken Azure AD device association"
        Write-Output -InputObject $AutopilotDeviceList
    }
    else {
        Write-Warning -Message "Could not detect any Autopilot device identities"
    }
}