Remove-IntunePrimaryUser.ps1
|
<#PSScriptInfo .VERSION 1.1 .GUID 98552e9b-8f39-4050-8452-ecb603a2129a .AUTHOR Alex Marrero .COMPANYNAME .COPYRIGHT .TAGS Intune .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES 1.1 - Fixed module version alignment, try/finally structure, and added ScanAllDevices support. .PRIVATEDATA #> <# .DESCRIPTION Clears (removes) the Intune Primary User assignment from devices. #> <# .SYNOPSIS Clears (removes) the Intune Primary User assignment from devices. .DESCRIPTION For each targeted Intune managed device, this script checks the current Primary User via: GET /beta/deviceManagement/managedDevices/{id}/users If the Primary User matches the provided UserPrincipalName, it clears the relationship via: DELETE /beta/deviceManagement/managedDevices/{id}/users/$ref This does NOT delete the device object from Intune. .PARAMETER UserPrincipalName The UPN of the user whose devices should have Primary User cleared. .PARAMETER ScanAllDevices If specified, scans ALL managed devices in the tenant and checks each device's Primary User. This is most accurate but can be slower in large tenants. .EXAMPLE .\Remove-IntunePrimaryUser.ps1 -UserPrincipalName "user@domain.com" -WhatIf .EXAMPLE .\Remove-IntunePrimaryUser.ps1 -UserPrincipalName "user@domain.com" -ScanAllDevices -Confirm #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$UserPrincipalName, [switch]$ScanAllDevices ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' #region ── Module Import (version-aligned) ────────────────────────────────────── $requiredModules = @( 'Microsoft.Graph.Authentication', 'Microsoft.Graph.DeviceManagement', 'Microsoft.Graph.Users' ) # Determine the highest version common to ALL required modules so assemblies # bind against the same Microsoft.Graph.Authentication.dll at runtime. $commonVersions = $null foreach ($m in $requiredModules) { $available = Get-Module -ListAvailable -Name $m -ErrorAction SilentlyContinue if (-not $available) { Write-Host "Installing missing module: $m" -ForegroundColor Yellow Install-Module -Name $m -Scope CurrentUser -Force -AllowClobber $available = Get-Module -ListAvailable -Name $m } $versions = $available | ForEach-Object { $_.Version } | Sort-Object -Descending if ($null -eq $commonVersions) { $commonVersions = $versions } else { $commonVersions = $commonVersions | Where-Object { $_ -in $versions } } } if (-not $commonVersions) { throw "No common module version found across: $($requiredModules -join ', '). Run: Install-Module Microsoft.Graph -Scope CurrentUser -Force" } $targetVersion = ($commonVersions | Sort-Object -Descending | Select-Object -First 1).ToString() Write-Host "Loading Microsoft.Graph modules at aligned version: $targetVersion" -ForegroundColor Cyan foreach ($m in $requiredModules) { Import-Module $m -RequiredVersion $targetVersion -ErrorAction Stop } #endregion ────────────────────────────────────────────────────────────────────── try { #region ── Connect ────────────────────────────────────────────────────────── Write-Host "Connecting to Microsoft Graph..." -ForegroundColor Cyan Connect-MgGraph -Scopes @( 'DeviceManagementManagedDevices.ReadWrite.All', 'User.Read.All' ) -NoWelcome #endregion #region ── Resolve user ──────────────────────────────────────────────────── $user = Get-MgUser -UserId $UserPrincipalName Write-Host "Resolved user: $($user.DisplayName) ($($user.Id))" -ForegroundColor Green #endregion #region ── Enumerate devices ─────────────────────────────────────────────── $devicesToProcess = [System.Collections.Generic.List[PSObject]]::new() if ($ScanAllDevices) { Write-Host "ScanAllDevices specified — enumerating all managed devices..." -ForegroundColor Yellow $allDevices = Get-MgDeviceManagementManagedDevice -All $total = $allDevices.Count $i = 0 foreach ($device in $allDevices) { $i++ Write-Progress -Activity "Scanning devices" -Status "$i / $total - $($device.DeviceName)" -PercentComplete (($i / $total) * 100) try { $primaryUser = Invoke-MgGraphRequest -Method GET ` -Uri "beta/deviceManagement/managedDevices/$($device.Id)/users" ` -ErrorAction Stop $primaryUpn = ($primaryUser.value | Select-Object -First 1).userPrincipalName if ($primaryUpn -eq $UserPrincipalName) { $devicesToProcess.Add($device) } } catch { Write-Verbose "Could not query primary user for $($device.DeviceName): $_" } } Write-Progress -Activity "Scanning devices" -Completed } else { # Faster path: query devices registered to the user via filter Write-Host "Querying devices with userPrincipalName = '$UserPrincipalName'..." -ForegroundColor Cyan $userDevices = Get-MgDeviceManagementManagedDevice -Filter "userPrincipalName eq '$UserPrincipalName'" -All foreach ($device in $userDevices) { # Confirm primary user actually matches (filter is on enrolled user, not always primary user) try { $primaryUser = Invoke-MgGraphRequest -Method GET ` -Uri "beta/deviceManagement/managedDevices/$($device.Id)/users" ` -ErrorAction Stop $primaryUpn = ($primaryUser.value | Select-Object -First 1).userPrincipalName if ($primaryUpn -eq $UserPrincipalName) { $devicesToProcess.Add($device) } else { Write-Verbose "Skipping $($device.DeviceName) — primary user is '$primaryUpn', not target." } } catch { Write-Verbose "Could not query primary user for $($device.DeviceName): $_" } } } #endregion #region ── Clear Primary User ────────────────────────────────────────────── if ($devicesToProcess.Count -eq 0) { Write-Host "No devices found with '$UserPrincipalName' as Primary User." -ForegroundColor Yellow return } Write-Host "`nFound $($devicesToProcess.Count) device(s) with Primary User = '$UserPrincipalName':" -ForegroundColor Cyan $devicesToProcess | ForEach-Object { Write-Host " • $($_.DeviceName) ($($_.Id)) — OS: $($_.OperatingSystem)" -ForegroundColor White } Write-Host "" $cleared = 0 $failed = 0 foreach ($device in $devicesToProcess) { $target = "$($device.DeviceName) ($($device.Id))" if ($PSCmdlet.ShouldProcess($target, "Remove Primary User '$UserPrincipalName'")) { try { Invoke-MgGraphRequest -Method DELETE ` -Uri "beta/deviceManagement/managedDevices/$($device.Id)/users/`$ref" ` -ErrorAction Stop Write-Host " ✓ Cleared Primary User on $($device.DeviceName)" -ForegroundColor Green $cleared++ } catch { Write-Warning " ✗ Failed to clear Primary User on $($device.DeviceName): $_" $failed++ } } } Write-Host "`n── Summary ──" -ForegroundColor Cyan Write-Host " Devices processed : $($devicesToProcess.Count)" Write-Host " Cleared : $cleared" -ForegroundColor Green if ($failed -gt 0) { Write-Host " Failed : $failed" -ForegroundColor Red } #endregion } catch { Write-Error "Script failed: $_" } finally { try { Disconnect-MgGraph | Out-Null } catch { } Write-Host "Disconnected from Microsoft Graph" -ForegroundColor Cyan } |