Public/Get-ITFDeviceInvalidSharedOwner.ps1

<#
.SYNOPSIS
    Finds Self-Deploying (Shared) devices that are incorrectly assigned to a user.

.DESCRIPTION
    This reporting function automatically identifies all Autopilot Deployment Profiles
    configured for 'Shared' (Self-Deploying) usage using a direct Graph API call.
    It then queries each profile to find exactly which Autopilot devices are assigned
    to it, bridging the gap to the active Intune managed devices.
    
    Finally, it scans those devices to check if they have an Intune Primary User
    or an Entra ID Registered Owner attached to them.

.EXAMPLE
    Get-ITFDeviceInvalidSharedOwner
    Scans the tenant and returns a list of shared devices that have user ownership.
#>

function Get-ITFDeviceInvalidSharedOwner {
    [CmdletBinding()]
    param ()

    try {
        Write-Verbose "Fetching Autopilot Deployment Profiles via Graph API..."
        
        # 1. Identify all profiles that are configured as Self-Deploying / Shared
        $profilesResponse = Invoke-MgGraphRequest -Method GET -Uri "beta/deviceManagement/windowsAutopilotDeploymentProfiles" -ErrorAction Stop
        $sharedProfiles = $profilesResponse.value | Where-Object { $_.outOfBoxExperienceSettings.deviceUsageType -match "shared" }

        if ($null -eq $sharedProfiles -or $sharedProfiles.Count -eq 0) {
            Write-Warning "No Self-Deploying (Shared) Autopilot profiles found in this tenant."
            return
        }

        $profileNames = $sharedProfiles | Select-Object -ExpandProperty displayName
        Write-Host "Found $($sharedProfiles.Count) Shared Autopilot Profile(s): $($profileNames -join ', ')" -ForegroundColor Cyan

        # 2. Get all Autopilot Devices assigned to these specific profiles
        Write-Host "Retrieving assigned hardware devices for these profiles..." -ForegroundColor Cyan
        $sharedAutopilotDevices = @()

        foreach ($autopilotProfile in $sharedProfiles) {
            $uri = "beta/deviceManagement/windowsAutopilotDeploymentProfiles/$($autopilotProfile.id)/assignedDevices"
            
            # Loop to handle pagination (if a profile has more than 1000 devices)
            while ($uri) {
                $response = Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop
                if ($response.value) {
                    # Add profile name to the object so we can use it later
                    $devicesWithProfile = $response.value | ForEach-Object {
                        $_ | Add-Member -NotePropertyName "DeploymentProfileName" -NotePropertyValue $autopilotProfile.displayName -PassThru
                    }
                    $sharedAutopilotDevices += $devicesWithProfile
                }
                $uri = $response.'@odata.nextLink' # Get the next page URL if it exists
            }
        }

        # Filter out devices that are not yet enrolled in Intune (no managedDeviceId)
        $enrolledAutopilotDevices = $sharedAutopilotDevices | Where-Object { -not [string]::IsNullOrWhiteSpace($_.managedDeviceId) }

        if ($enrolledAutopilotDevices.Count -eq 0) {
            Write-Host "No enrolled devices found that are linked to the shared profiles." -ForegroundColor Green
            return
        }

        # 3. Match them with the actual Intune Managed Devices
        Write-Verbose "Fetching active Intune managed devices..."
        $allIntuneDevices = Get-MgDeviceManagementManagedDevice -All -Property "id,deviceName,serialNumber,userPrincipalName,azureAdDeviceId" -ErrorAction Stop
        
        # Cross-reference the IDs
        $targetManagedDeviceIds = $enrolledAutopilotDevices.managedDeviceId
        $sharedDevices = $allIntuneDevices | Where-Object { $targetManagedDeviceIds -contains $_.Id }

        Write-Host "Scanning $($sharedDevices.Count) active shared device(s) for invalid ownership..." -ForegroundColor Cyan

        $invalidDevices = @()
        $counter = 1

        # 4. Analyze each device for lingering ownership
        foreach ($device in $sharedDevices) {
            
            Write-Progress -Activity "Analyzing Shared Devices" -Status "Checking $($device.DeviceName) ($counter / $($sharedDevices.Count))" -PercentComplete (($counter / $sharedDevices.Count) * 100)
            
            $intuneUser = $device.UserPrincipalName
            $entraOwners = @()
            
            # Look up the correct profile name from our mapping
            $matchedProfileName = ($enrolledAutopilotDevices | Where-Object { $_.managedDeviceId -eq $device.Id }).DeploymentProfileName | Select-Object -First 1

            if (-not [string]::IsNullOrWhiteSpace($device.AzureAdDeviceId)) {
                $entraDevice = Get-MgDevice -Filter "deviceId eq '$($device.AzureAdDeviceId)'" -Property "id" -ErrorAction SilentlyContinue
                
                if ($null -ne $entraDevice) {
                    $owners = Get-MgDeviceRegisteredOwner -DeviceId $entraDevice.Id -ErrorAction SilentlyContinue
                    
                    if ($null -ne $owners -and $owners.Count -gt 0) {
                        foreach ($owner in $owners) {
                            $ownerDetails = Get-MgUser -UserId $owner.Id -Property "userPrincipalName" -ErrorAction SilentlyContinue
                            if ($null -ne $ownerDetails -and -not [string]::IsNullOrWhiteSpace($ownerDetails.UserPrincipalName)) {
                                $entraOwners += $ownerDetails.UserPrincipalName
                            }
                            else {
                                $entraOwners += $owner.Id
                            }
                        }
                    }
                }
            }

            if ((-not [string]::IsNullOrWhiteSpace($intuneUser)) -or ($entraOwners.Count -gt 0)) {
                $invalidDevices += [PSCustomObject]@{
                    DeviceName    = $device.DeviceName
                    SerialNumber  = $device.SerialNumber
                    ProfileName   = $matchedProfileName
                    IntuneOwner   = if ([string]::IsNullOrWhiteSpace($intuneUser)) { "<None>" } else { $intuneUser }
                    EntraIDOwners = if ($entraOwners.Count -eq 0) { "<None>" } else { $entraOwners -join ", " }
                }
            }
            
            $counter++
        }

        Write-Progress -Activity "Analyzing Shared Devices" -Completed

        if ($invalidDevices.Count -eq 0) {
            Write-Host "✅ All shared devices are clean! No invalid users attached." -ForegroundColor Green
        }
        else {
            Write-Warning "Found $($invalidDevices.Count) shared device(s) with invalid user ownership!"
            return $invalidDevices
        }
    }
    catch {
        Write-Error "An error occurred while scanning devices: $($_.Exception.Message)"
    }
}