Managing devices in Microsoft Intune can be a complex task, especially when ensuring that your device inventory remains compliant and up-to-date. I created a PowerShell script that automates the cleanup of non-compliant and stale devices by leveraging Microsoft Graph commands to report on devices based on their last sync time and compliance status. While you can find this information in the Intune Portal, using these handy PowerShell commands from Microsoft.Graph expedites the process. This allows administrators to quickly identify and remove outdated entries. This approach not only streamlines device management but also aligns with best practices for maintaining a secure and efficient Intune environment. This is a quick script I wrote to better understand the Microsoft graph commands and provide instant insight into device compliance and clean removals programmatically.
Table of Contents
Requirements
You will need to have Microsoft.Graph.Users and Microsoft.Graph.DeviceManagement PowerShell modules installed and access to an available Entra ID tenant. Also, Ensure your account has the necessary permissions to manage devices, specifically the scopes DeviceManagementManagedDevices.ReadWrite.All
and Device.ReadWrite.All
.
Functionality
Report all Intune Devices out of compliance with Microsoft Graph PowerShell
When I was building this script and using it, I decided to create a -report parameter that when passed, creates a CSV file with the headers “DeviceName”, “ComplianceState”, “LastSyncDateTime” , and “User”. It loops through all the devices in Intune and appends the values if the state is not “compliant” and the last sync time is less than the -daysSinceSync argument, which defaults to 14.
./Remove-MSDevice -report -daysSinceSync 14
Here are the parameters, authentication, and reporting logic regions of the script
param ( [Parameter(Mandatory=$false)] [Alias("name", "device")] [string] $deviceName, [Parameter(Mandatory=$false)] [Alias("days")] [ValidateRange(1, 60)] [int] $daysSinceSync = 14, [Parameter(Mandatory=$false, HelpMessage="Switch to report non compliant devices")] [switch] $report ) # ---------------- Import modules -------------------- $modules = @("Microsoft.Graph.DeviceManagement", "Microsoft.Graph.Users") $modules | % { try{ if($null -eq (Get-Module -ListAvailable -Name $_)){ Install-Module -Name $_ -Scope CurrentUser -AllowClobber -Force Import-Module -Name $_ } }catch{ Write-Error -Message "Could not import necessary modules. Exit 1"; exit 1 } } # ----------- Authenticate user to Microsoft graph --------------- try{ $u = Get-MgUser -top 1 -ErrorAction SilentlyContinue if($null -eq $u){ throw "Please connect to MSGraph" } }catch{ Write-Output $_.Exception.Message Connect-MgGraph -Scopes "DeviceManagementManagedDevices.ReadWrite.All", "Device.ReadWrite.All" -UseDeviceCode } $REGEXINPUT = '^[yY]$' if($report.isPresent){ $outFile = "stale-devices-$(get-date -Format "yyyyMMdd").csv" if(test-path $outFile){ remove-Item $outFile } $n = {} | select "DeviceName", "ComplianceState", "LastSyncDateTime" , "User" | Export-Csv -path $outFile $csvData = Import-csv -path $outFile $devices = Get-MgDeviceManagementManagedDevice | select * | ?{($_.ComplianceState -ne 'compliant') -and ($_.LastSyncDateTime -lt (Get-Date).AddDays(-$daysSinceSync))} $devices | % { $data = [PSCustomObject]@{ DeviceName = $_.DeviceName ComplianceState = $_.ComplianceState LastSyncDateTime = $_.LastSyncDateTime User = $_.UserPrincipalName } $data | Export-Csv $outFile -Append } }
Remove or Evaluate Stale Intune Device with Microsoft Graph PowerShell
You can also invoke this script to remove the device object from Entra ID and Intune. This command will search for the device with the DeviceName attribute of “PC-Test” in Intune and remove it. If there are duplicates, it will evaluate each and prompt the user to delete all objects or just the objects with the properties meeting the conditions – not in a compliant state and LastSycDateTime over 21 days.
./Remove-MSDevice -deviceName "PC-Test" -daysSinceSync 21
Logic for the device evaluation and removal. I create two functions, one RemoveDeviceFromEntraAndIntune
that does exactly that using the Remove-MgDeviceManagementManagedDevice and Remove-MgDeviceByDeviceId. It handles errors if either command fail by concatenating it to the $err string and throwing the error. The EvaluateStaleDevice
function uses the daysSinceSync parameter and checks the compliance state of the device. This function is invoked when there are duplicate devices. The script loops through the devices with the same deviceName and evaluates the conditions. If they are not met, user is shown data of the stale device and prompted for removal. If yes, only the devices failing the conditions are removed by calling RemoveDeviceFromEntraAndIntune
The script defines two functions for device evaluation and removal:
- RemoveDeviceFromEntraAndIntune: This function removes devices using
Remove-MgDeviceManagementManagedDevice
andRemove-MgDeviceByDeviceId
. It handles errors by concatenating any failures to the$err
string and throws an error if either command fails. - EvaluateStaleDevice: This function checks the compliance state of a device against the
daysSinceSync
parameter. It is invoked when there are duplicate devices with the samedeviceName
. The script loops through these devices and evaluates their conditions. If a device does not meet the criteria, its details are displayed to the user, who is then prompted for removal. If the user confirms, only the devices that fail the conditions are removed by callingRemoveDeviceFromEntraAndIntune
within its scope.
function RemoveDeviceFromEntraAndIntune($devObj) { # Write-Output "removal function called for $($devObj.DeviceName)" try{ Remove-MgDeviceManagementManagedDevice -ManagedDeviceId $devObj.Id Remove-MgDeviceByDeviceId -DeviceId $devObj.AzureAdDeviceId Start-Sleep -Seconds 3 $err = "" if($null -ne (Get-MgDeviceManagementManagedDevice -ManagedDeviceId $devObj.Id)){ $err += "- Failed to removed from Intune" } if($null -ne (Get-MgDeviceByDeviceId -DeviceId $devObj.AzureAdDeviceId)){ $err += "- Failed to remove from Entra ID" } if(-not ([string]::IsNullOrEmpty($err))){ throw "$($devObj.DeviceName) $err" } }catch{ Write-Output $_.Exception.Message # outer try will catch this # throw "Something went wrong. Failed to cleanly remove $($devObj.DeviceName)" } } function EvaluateStaleDevice($device){ if(($_.daysSinceSyncDateTime -lt (Get-Date).AddDays(-$daysSinceSync)) -and ($_.ComplianceState -ne "compliant")){ Write-Output("Possible stale device object`nDisplayName: {0}`nDeviceId: {1}`ndaysSinceSyncTime: {2}`nCompliance state: {3}" -f $_.DeviceName, $_.Id, $_.daysSinceSyncDateTime, $_.ComplianceState) # based on the device status, prompt user for verification of deletion $userIn = Read-Host "Would you like to delete this stale object (y/Y): " if($userIn -match $REGEXINPUT ){ RemoveDeviceFromEntraAndIntune -devObj $_ } } } if($PSBoundParameters.ContainsKey('deviceName')){ try { $deviceObject = Get-MgDeviceManagementManagedDevice -Filter "DeviceName eq '$($deviceName)'" | select * if($null -eq $deviceObject){ throw "$deviceName was not found in Intune" } if($deviceObject.Count -gt 1){ # notify user multiple objects with same DeviceName exist in Intune Write-Output("{0} - {1} Device Objects found. Please view the stale object and determine which one you want to remove" -f $deviceObject[0].DeviceName, $deviceObject.Count) # promt user to remove both objects $userInBoth = Read-Host "Would you like to remove both MDM device objects (y/Y): " # if user wants to remove all device objects if($userInBoth -match $REGEXINPUT){ # call the helper function to remove the devices from Intune and Entra $deviceObject | %{ RemoveDeviceFromEntraAndIntune -devObj $_ } }else{ # Stale device objects defined as devices that haven't synced in 14 days and status not compliant status $deviceObject | ForEach-Object { EvaluateStaleDevice -device $_ } } }else{ # remove the device if only one object found RemoveDeviceFromEntraAndIntune -devObj $deviceObject } }catch { Write-Output $_.Exception.Message; exit 1 } }
Piping Filtered Devices to the Script using Get-MgDeviceManagementManagedDevice
You can use the -Filter flag or pipe from Where-Object
using any property returned by the Get-MgDeviceManagementManagedDevice
command, or any command that returns an accurate device name. Maybe a hybrid environment that has stale devices in the cloud from sync or imaging issues, you can leverage the Get-ADComputer command from the ActiveDirectory module to cleanup the environment.
This example filter the devices not in a compliant state and invokes the script for each device.
Get-MgDeviceManagementManagedDevice | Where-Object {$_.ComplianceState -ne 'compliant'} | ForEach-Object { ./Remove-MSDevice.ps1 -deviceName $_.DeviceName }
Completed Script
<# .SYNOPSIS Removes device objects from Intune and Entra ID. Detects devices out of compliance and lastSync time, and duplicate devices .DESCRIPTION Takes the -deviceName argument to remove and deletes it from Intune and Entra ID Detects duplicate devices and evaluates them, using the -daysSinceSync (default 14 days) and compliance status Prompts the user to delete all device objects with a duplicate name or just the stale device Using the -reports will create a CSV file of not compliant devices that havnt synced within the last -daysSinceSync (default 14) .PARAMETER deviceName The name of the device to remove. .PARAMETER daysSinceSync The number of days since the device last synced. Default is 14 days. .PARAMETER reports Generates a report in CSV format of devices in Intune that meet the conditions of not compliant and last checkin tile less than 14 days ago (default). Headers are "DeviceName", "ComplianceState", "LastSyncDateTime" , and "User" .NOTES This function is not supported in Linux. .LINK .EXAMPLE offboardingWorkstation.ps1 -deviceName "LT-5234431" -daysSinceSync 21 The results of ths command will remove the device from Entra and Intune. If duplicate devices are detected, user will be asked to delete all of the duplicates. if no, user will be prompted to remove the device objects deemed stale through the conditions compliance status and last sync time .EXAMPLE offboardingWorkstation.ps1 -report Creates a csv file named stale-devices-YYYYMMdd.csv with not compliant device that havent synced "DeviceName", "ComplianceState", "LastSyncDateTime" , and "User" #> param ( [Parameter(Mandatory=$false)] [Alias("name", "device")] [string] $deviceName, [Parameter(Mandatory=$false)] [Alias("days")] [ValidateRange(1, 60)] [int] $daysSinceSync = 14, [Parameter(Mandatory=$false, HelpMessage="Switch to report non compliant devices")] [switch] $report ) # ---------------- Import modules -------------------- $modules = @("Microsoft.Graph.DeviceManagement", "Microsoft.Graph.Users") $modules | % { try{ if($null -eq (Get-Module -ListAvailable -Name $_)){ Install-Module -Name $_ -Scope CurrentUser -AllowClobber -Force Import-Module -Name $_ } }catch{ Write-Error -Message "Could not import necessary modules. Exit 1"; exit 1 } } try{ $u = Get-MgUser -top 1 -ErrorAction SilentlyContinue if($null -eq $u){ throw "Please connect to MSGraph" } }catch{ Write-Output $_.Exception.Message Connect-MgGraph -Scopes "DeviceManagementManagedDevices.ReadWrite.All", "Device.ReadWrite.All" -UseDeviceCode } $REGEXINPUT = '^[yY]$' function RemoveDeviceFromEntraAndIntune($devObj) { # Write-Output "removal function called for $($devObj.DeviceName)" try{ Remove-MgDeviceManagementManagedDevice -ManagedDeviceId $devObj.Id Remove-MgDeviceByDeviceId -DeviceId $devObj.AzureAdDeviceId Start-Sleep -Seconds 3 $err = "" if($null -ne (Get-MgDeviceManagementManagedDevice -ManagedDeviceId $devObj.Id)){ $err += "- Failed to removed from Intune" } if($null -ne (Get-MgDeviceByDeviceId -DeviceId $devObj.AzureAdDeviceId)){ $err += "- Failed to remove from Entra ID" } if(-not ([string]::IsNullOrEmpty($err))){ throw "$($devObj.DeviceName) $err" } }catch{ Write-Output $_.Exception.Message # outer try will catch this # throw "Something went wrong. Failed to cleanly remove $($devObj.DeviceName)" } } function EvaluateStaleDevice($device){ if(($_.daysSinceSyncDateTime -lt (Get-Date).AddDays(-$daysSinceSync)) -and ($_.ComplianceState -ne "compliant")){ Write-Output("Possible stale device object`nDisplayName: {0}`nDeviceId: {1}`ndaysSinceSyncTime: {2}`nCompliance state: {3}" -f $_.DeviceName, $_.Id, $_.daysSinceSyncDateTime, $_.ComplianceState) # based on the device status, prompt user for verification of deletion $userIn = Read-Host "Would you like to delete this stale object (y/Y): " if($userIn -match $REGEXINPUT ){ RemoveDeviceFromEntraAndIntune -devObj $_ } } } if($PSBoundParameters.ContainsKey('deviceName')){ try { $deviceObject = Get-MgDeviceManagementManagedDevice -Filter "DeviceName eq '$($deviceName)'" | select * if($null -eq $deviceObject){ throw "$deviceName was not found in Intune" } if($deviceObject.Count -gt 1){ # notify user multiple objects with same DeviceName exist in Intune Write-Output("{0} - {1} Device Objects found. Please view the stale object and determine which one you want to remove" -f $deviceObject[0].DeviceName, $deviceObject.Count) # promt user to remove both objects $userInBoth = Read-Host "Would you like to remove both MDM device objects (y/Y): " # if user wants to remove all device objects if($userInBoth -match $REGEXINPUT){ # call the helper function to remove the devices from Intune and Entra $deviceObject | %{ RemoveDeviceFromEntraAndIntune -devObj $_ } }else{ # Stale device objects defined as devices that haven't synced in 14 days and status not compliant status $deviceObject | ForEach-Object { EvaluateStaleDevice -device $_ } } }else{ # remove the device if only one object found RemoveDeviceFromEntraAndIntune -devObj $deviceObject } }catch { Write-Output $_.Exception.Message; exit 1 } } if($report.isPresent){ $outFile = "stale-devices-$(get-date -Format "yyyyMMdd").csv" if(test-path $outFile){ remove-Item $outFile } $n = {} | select "DeviceName", "ComplianceState", "LastSyncDateTime" , "User" | Export-Csv -path $outFile $csvData = Import-csv -path $outFile $devices = Get-MgDeviceManagementManagedDevice | select * | ?{($_.ComplianceState -ne 'compliant') -and ($_.LastSyncDateTime -lt (Get-Date).AddDays(-$daysSinceSync))} $devices | % { $data = [PSCustomObject]@{ DeviceName = $_.DeviceName ComplianceState = $_.ComplianceState LastSyncDateTime = $_.LastSyncDateTime User = $_.UserPrincipalName } $data | Export-Csv $outFile -Append } }
Conclusion
This script can streamlines device management in Intune and Entra ID by reporting and automating the removal of non-compliant and stale devices, showcasing the power of Microsoft Graph PowerShell. Leveraging such tools not only enhances efficiency but also represents a valuable practice in your IT journey, as it equips you with essential skills for managing cloud resources effectively. Drop a comment or contact me with any suggestions
Now loading...