Windows Autopilot is a great way of provisioning new devices. Like a new phone you just unpack the device and enroll it to the management system. Windows Autopilot will take care of all necessary configurations defined by your company. It saves time, money and users will have a great onboarding experience. In my opinion this is clearly the way forward for device provisioning. To make all this happening Windows Autopilot has a central register where all devices are getting registered by several attributes like Model, Manufacturer, Serial Number and unique Hardware Hash. This is how Windows Autopilot recognizes a device as soon as it starts and connects to the cloud service. Once identified it can be mapped to a customer tenant with specific settings for the provisioning.
That’s a great approach and so far easily accomplished by registering the devices with Windows Autopilot by your own via these PowerShell scripts:
A script to read the hardware hash and export to a .csv file:
A script to upload the hardware hashes as .csv file to Intune (function Import-AutoPilotCSV):
or the better way the OEM, reseller, distributor, or partner does the registration for you. This way you don’t have to deal with all the hashes and so on. You as a customer just receive the device and it is already registered to your tenant and it is ready for enrollment/provisioning.
That’s great and easy to utilize. The downside is the devices are registered to your tenant and if the device goes end-of-life you have to de-register the devices you are not using anymore. Imagine a leasing contract over 3 years for the devices and you have to return them after the 3 years. These devices normally have a second life and are used somewhere else for some more time. Or you just sell the devices you don’t need anymore. A lot of reasons can apply why you return, sell, re-purpose devices. In all cases you don’t want them registered to your tenant anymore. So, how do we de-register them from our tenant?
How to cleanup Windows Autopilot registrations?
Basically you have the Microsoft Endpoint Manager admin center UI (Intune) options to select the devices you don’t need and delete them:

If you look at the highlighted sentence it tells us that only devices which are not enrolled can be deleted. This make totally sense, as these devices should be in production usage currently. The normal end of life scenario would be to factory reset the device and then delete the Windows Autopilot registration. This may not be possible as the device got broken and can’t be reset, then we need to delete the Intune device object by ourselves and then delete the Windows Autopilot device registration. Pretty simple but with larger amount of devices not the easiest approach. Finding all the device Serial Numbers in the admin center and selecting them for deletion. Luckily with the help of two PowerShell modules we can automate this in the same way like we can import devices defined by a .csv file. I have written a PowerShell function to cleanup the Windows Autopilot registrations based on the same .csv file you can use for importing. This way you can export your devices from any asset management system you have, construct the .csv file and delete them from Windows Autopilot.
The function uses the Intune PowerShell SDK and the PowerShell Module WindowsAutoPilotIntune:
- https://www.powershellgallery.com/packages/WindowsAutoPilotIntune
- https://github.com/microsoft/Intune-PowerShell-SDK
It is easy to use by executing the function with the parameter -CsvFile and the path to the file with the device information to delete:
Start-AutopilotCleanupCSV -CsvFile C:\Devices.csv
Device Serial Number Deletion Request Status
-------------------- -----------------------
7243-2648-3107-2818-2556-6923-30 200
It will read the .csv file line by line, get the serial number and delete the Windows Autopilot registration based on the serial number.
A more complete automation is the following Windows Autopilot cleanup script (optionally with the parameter to cleanup the Intune device objects as well). It runs the Autopilot cleanup function, then starts the Autopilot Sync to your tenant, and does a re-check if all the device serial numbers are deleted from your tenant.
Connect-MSGraph | Out-Null
$CsvFile = "C:\temp\autopilot-devices.csv"
Start-AutopilotCleanupCSV -CsvFile $CsvFile -IntuneCleanup
Write-Output "`nInvoking Autopilot sync..."
Start-Sleep -Seconds 15
Invoke-AutopilotSync
Write-Output "`nWaiting 60 seconds to re-check if devices are deleted..."
Start-Sleep -Seconds 60
# Check if all Autopilot devices are successfully deleted
$serialNumbers = Import-Csv $CsvFile | Select-Object -Unique 'Device Serial Number' | Select-Object -ExpandProperty 'Device Serial Number'
Write-Output "`nThese devices couldn't be deleted:"
foreach ($serialNumber in $serialNumbers){
$device = Get-AutoPilotDevice -serial $serialNumber
$device.serialNumber
}
The output would be something similar to this:
Device Serial Number Deletion Request Status
-------------------- -----------------------
7243-2648-3107-2818-2556-6923-30 200
7851-2064-8105-4061-0737-2977-27 400
Invoking Autopilot sync...
Waiting 60 seconds to re-check if devices are deleted...
These devices couldn't be deleted:
7851-2064-8105-4061-0737-2977-27
All devices which could not be deleted, due to whatever reason are listed so you can have a look manually and resolve these errors.
The complete script can be found here:
https://github.com/okieselbach/Intune/blob/master/Start-AutopilotCleanupCSV.ps1
As always, as soon as cleanup (deletion operations) are automated we need to take special care and you should test thoroughly in your lab environment first. This is not an official MS module, it is shared to the community as is. Feel free to leave comments or open issues on my GitHub for questions, enhancements. I’m using it in my environments successfully. Evaluate, test, and test again 🙂 before deleting devices in your tenant.
I hope it helps with deleting Windows Autopilot registrations or gives you a starting point for your own automation script.
Thanks for sharing this idea and script. You tried running it in Azure Automation?
No I didn‘t, I thought of it as an action not done very often and how about the csv input, some other form of input would then be needed. But in general it could run in an Azure Automation account.
Hi did this get removed? I am trying to follow the instructions as I have about 1000 devices I need to remove from intone via a csv file but I cannot run the Start-AutopilotCleanupCSV as it cannot be found. can you assist?
Hey Mike,
I just double checked, it is still there. You just need to download and use it. See GitHub URL here: https://github.com/okieselbach/Intune/blob/master/Start-AutopilotCleanupCSV.ps1
best,
Oliver
Oliver,
thank you for the great script! It works great!
But I do have a question, you mentioned using -IntuneCleanup removes Intune entries. What about AzureAD entries?
Azure AD devices can be deleted as well if you like to. I didn’t implement it as I like to preserve the BitLocker key saved with the AAD device object for a longer time to make sure (in case of) I have the key for accessing the hard drive. But feel free to enhance it, there is a comment “enhancement option: delete AAD record as well” in the script where you could add the AAD device deletion logic.
best,
Oliver
Get-AutoPilotDevice : System.Management.Automation.ParameterBindingValidationException: Cannot validate argument on parameter ‘Url’. The provided URL is not valid – the URL may be a
relative URL —> System.Management.Automation.ValidationMetadataException: The provided URL is not valid – the URL may be a relative URL
at Microsoft.Intune.PowerShellGraphSDK.ValidateUrlAttribute.ValidateElement(Object url)
at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
Hey Mike,
are you sure you have the script cloned or downloaded correctly. The Get-AutoPilotDevice function is actually from the SDK, I didn’t change this one and it is working well for a long time, used with many customers. I suspect something is gone wrong with the $uri definitions there. Maybe compare again with the GitHub version if the URI’s are like they should. Can’t really imagine something else what went wrong…
best,
Oliver
Hi Oliver you have been a savior with this script. i deleted the modules and reimported them. We are also working off a filtered network so that may have been an issue as well. Once i deleted the module and re imported them the script seems to be working correctly. The only issue i am getting is a 504 gateway timeout and that may be that i have too many serial numbers trying to delete. Thank you Thank you for this. This saved me from deleting them 1 by 1. keep up the great work and thanks again.
Hi is there any way to change the group tags of already enrolled devices using a csv with multiple serial numbers only? We have many enrolled and assigned to a group tag but we need to change hundreds of them. It will be too time consuming to change the group tags 1 by 1. Thank you so much
Hey Mike,
yes sure have a look here: https://oofhours.com/2019/11/25/now-you-can-edit-group-tags-and-computer-names-for-windows-autopilot-devices/
You basically need to combine the Set-AutopilotDevice with a Get-AutopilotDevice and that’s it.
best,
Oliver
I have delete a broken device from intune, without thinking of Do i understand this correct, that if i have deleted a Autopilot devices (that’s broken and don’t have the serial number), i can run this script and remove it completly? – https://github.com/okieselbach/Intune/blob/master/Start-AutopilotCleanupCSV.ps1
Hey Jonas,
I’m not sure if I understand your request correct. Can’t you identify the device by its old Name or Model/Manufacturer and delete the identified broken one? Or do you want to remove all Autopilot devices?
best,
Oliver
Hello Olivier,
Thanks for sharing this.
I am trying to run this on my environment, but I’m receiving this error:
Invoke-AutopilotSync : System.Net.Http.HttpRequestException: 429
{
“error”: {
“code”: “TooManyRequests”,
“message”: “{\r\n \”_version\”: 3,\r\n \”Message\”: \”An error has occurred – Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 – Activity ID: e659dd23-e4a9-473a-994a-a8eed1ffea00 – Url:
https://fef.amsub0102.manage.microsoft.com/DeviceEnrollmentFE_2102/StatelessDeviceEnrollmentFEService/deviceManagement/windowsAutopilotSettings/microsoft.management.services.api.sync?api-version=5020-09-14\”,\r\n \”CustomApiErrorPhrase\”: \”\”,\r\n \”RetryAfter\”:
null,\r\n \”ErrorSourceService\”: \”\”,\r\n \”HttpHeaders\”: \”{}\”\r\n}”,
“innerError”: {
“date”: “2021-02-18T12:12:54”,
“request-id”: “e659dd23-e4a9-473a-994a-a8eed1ffea00”,
“client-request-id”: “e659dd23-e4a9-473a-994a-a8eed1ffea00”
Any idea on what that might be?
Hi Mike,
429 refers to “too many requests”, so basically the backend (MS) couldn’t handle the amount of requests or you got throttled. I guess you could simply try again after some time…
best,
Oliver
Hi Oliver,
I’m getting device couldn’t be deleted even extending the time to 5mins.
”
Device Serial Number Deletion Request Status
——————– ———————–
P 400
Invoking Autopilot sync…
Waiting 300 seconds to re-check if devices are deleted…
These devices couldn’t be deleted:
”
Any suggestions?
Thanks
Hey Nickoff,
interesting… can you delete the devices via the portal? I suspect it is maybe not script related, maybe the devices can’t be deleted at all, even from the MEM portal?!
best,
Oliver
Yes, i’m able to delete from MEM portal.
hmmm, interesting. How many devices are you trying to delete? did you try to lower the batchMaxCount from 20 to maybe 10 just to see if this has an effect? The request status 400 seems like the serial number maybe no found. Maybe a formatting issue in the CSV?
Hi Oliver,
I just added 1 serialnumber in the CSV file for testing :). as for the format, I added first row in the CSV file with ‘Device Serial Number’ & actual serial number in the second row
It looks like not able to detect the serial number.
Quote:
Device Serial Number Deletion Request Status
——————– ———————–
(It doesn’t show any serial number HERE)
Invoking Autopilot sync…
Waiting 60 seconds to re-check if devices are deleted…
These devices couldn’t be deleted:
(It’s shows the serial number here as per the CSV file)
Unquote:
Hi NickOff,
I tested it in my tenant, first try I used a CSV with 2 serial numbers. Worked without problems. Second test I added just one serial number and this wasn’t successful. I debugged the script and added a fix. Re-test was successful then with only one serial number. Right now I can’t find any more issues with the script as it is correctly deleting the devices in my tenant. Can you try again with the fixed version?
best,
Oliver
Hi Oliver,
The new script works fine (don’t know why I did test with 1 device only in the first place 🙂
Still shows device couldn’t be deleted tho but after refreshing in MEM portal, after about 3 mins it’s gone. Probably it’s taking more time in our tenant.
“
Device Serial Number Deletion Request Status
——————– ———————–
MP1K3HK0 200
Waiting 60 seconds to re-check if devices are deleted…
These devices couldn’t be deleted (if no device is listed, everything went well):
MP1K3HK0
“
Thanks
Hi Oliver, do you have anything to delete Azure AD device cleanup?. wanted to remove devices AAD as well once we removed from Intune.
Hi mmurthy536,
No I don’t, but it can be easily managed by a very short PowerShell script. The example for it can be found here: https://docs.microsoft.com/en-us/azure/active-directory/devices/manage-stale-devices
best,
Oliver
Hi Oliver,
I’m using this script to try and delete enrolled devices but none of the devices I’m trying are deleting and I’m not getting any errors.
I have multiple serial numbers in my .csv file and I’ve tried multiple .csv files. Do I need the .csv file with the device hashes or can I just use a .csv I get from this command Get-AutoPilotDevice -serial
Any other tips or ideas what I could be doing wrong?
Thank You!
Hmm interesting. I used the csv file generated from Get-WindowsAutoPilotInfo.ps1 -OutputFile C:\temp\test.csv
I tested it right now in my lab and it is still working for me. the entries in the csv are deleted. I tried a csv with one device and with two device entries. Maybe the csv parsing does not work, can you try to delete with a device info gathered with the command above. just to see if it is a simple parsing problem…
Hi again Oliver,
I found that my problem was the formatting of my . csv file. Your code works great! Thank you!
Hallo Oliver, Nathan,
can you explain the format of the csv file or post an example.
Thanks
i just created a a file called test.csv . in the file i only have Device Serial number and underneath i add the serial number that looks like this and it works perfectly fine.
Keep in mind if they are already joined in Azure you may have to manually delete them from there first. For some reason i get a 400 error unless they are deleted.. Once delete from Azure i copy paste all my serial numbers in the csv,
Device Serial Number
XXXXXXXXX
XXXXXXXXX
etc
Create a folder called c:\temp,
Download the Start-AutopilotCleanupcsv.ps1 that you can download from this page and put it in the folder.
Open powershell ise as administrator, run the following commands.
Set-ExecutionPolicy -Force Unrestricted
Install-Module -Name WindowsAutoPilotIntune
Install-Script -Name Get-WindowsAutoPilotInfo
cd\
cd\ temp
Import-Module .\Start-AutopilotCleanupcsv.ps1 Press the stop button on top as it will do a invoke-autopilot sync
Go To Modules
Refresh modules
Modules No Module Name and look for Start-AutopilotCSV
CsvFile:* Enter the path c:\Temp\test.csv and hit Run
Wait a few mins as i run it again after about 5-7 mins until it says no device found skipping,
This works for me every time. I just used it 15 mins ago with no issues. , I wish there was a way to remove Azure devices like this by serial number not object id or stale devices as easily. If Oliver has a way please let me know. Hope this helps as it helped me.
Thank you Oliver for this post. it saved me a great deal of time rather that deleting them 1 by 1 in intune and having to wait until that one is completed.
Hy Michael,
i did exactly the Steps you post and ill get the same error, always.
It doesn’t matter if i run the script via Moduls or direct.
——————————————————————————-
Get-AutoPilotDevice : System.Management.Automation.ParameterBindingValidationException: Das Argument für den Parameter “Url” kann nicht überprüft
werden. The provided URL is not valid – the URL may be a relative URL —> System.Management.Automation.ValidationMetadataException: The provided
URL is not valid – the URL may be a relative URL
bei Microsoft.Intune.PowerShellGraphSDK.ValidateUrlAttribute.ValidateElement(Object url)
bei System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter
parameterMetadata, ParameterBindingFlags flags)
— Ende der internen Ausnahmestapelüberwachung —
bei System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
bei System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
bei System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
bei System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
In C:\Temp\Start-AutopilotCleanupcsv.ps1:254 Zeichen:31
+ $device = Get-AutoPilotDevice -serial $serial
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-AutoPilotDevice
—————————————————————————————————–
I have no idea what is running Wrong.
Thanks for Helping.
Hi Andreas,
so, it looks like for you the line $device = Get-AutoPilotDevice -serial $serial is generating the error. Can you try to copy the function code to a separate file and execute just the line Get-AutoPilotDevice -serial $serial with a valid serial number defined upfront. This is generating the URL is not valid error in your case but this should give you a valid result. So, isolate the function and test it separately to narrow down the issue.
best,
Oliver
Hallo Oliver,
i run Get-AutoPilotDevice -serial “VMware-42******”
It is a valid “Serial Number”
Exactly the same error.
Thanks Andreas
Can you verify the URL (internally used in the function Get-AutoPilotDevice) with Graph Explorer? Is this working for you in your tenant with your VMware-42**** serial number?
https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities?$filter=contains(serialNumber,’0182-3295-8753-2043-2410-5676-06′)
replace my 0182-3295-8753-2043-2410-5676-06 serial number with your one…
best,
Oliver
Hallo Oliver,
here the Result:
{“error”:{“code”:”InvalidAuthenticationToken”,”message”:”Access token is empty.”,”innerError”:{“date”:”2021-06-04T14:27:32″,”request-id”:”*****”,”client-request-id”:”*****”}}}
Thanks
Andreas
okay this is a graph explorer issue when not correctly signed-in. Check your sign-in state on the graph explorer. Ideally using a account with enough permissions.
I am trying to bulk remove devices within a CSV from AAD, Intune and Autopilot using one script.
When I run the script as is with -IntuneCleanup, the devices do not delete with a 400 error. I believe this is because they still exist in AAD.
There is an option referenced in the script:
“enhancement option: delete AAD record as well” in the script where you could add the AAD device deletion logic.
Has anybody else added this? If so, could you share?
Hi Oliver. Just to double check, if we only remove the AutoPilot registration, basically deleting the computer from AutoPilot Devices, and the device is currently enrolled and part of the domain, the only deletion is just the AP registration, AAD object and Intune registration is not affected, correct?
We have a few devices that just need removing from AP registration but not from ADD or Intube. Thanks!
Hey David,
Only devices that are not enrolled will be deleted if you do it via Portal UI. Out of memory I‘m unsure if this constrained is valid for the direct graph call as well. I don‘t think so, but I would test once more to be sure.
Hallo Oliver,
i found my Problem with the following Error:
{“error”:{“code”:”InvalidAuthenticationToken”,”message”:”Access token is empty.”,”innerError”:{“date”:”2021-06-04T14:27:32″,”request-id”:”*****”,”client-request-id”:”*****”}}}
this is not a problem of missing rights, it is a problem with the Serial Number.
The VMWare Autopilot devices have serial number with spaces between digits.
When i use a serial number from a native hardware, then it works.
So in my Case we find a solution to delete this orphaned Autopliot entry’s.
Login in into https://businessstore.microsoft.com then click manage, then choose devices and there you can delete this orphaned Autopliot entry’s.
Thank for your Help
Good to hear that you solved it finally and that you shared the finding. Many thanks!
Hi Oliver,
I have a question related to this but deleting the devices from Azure AD for autopilot based devices.
We are looking for a way to allow HP to upload the hardware hashes for our new devices so that we can provision our PC’s via autopilot. I would like to know what happens when I have to restage the same pc another time for a new user. When I do the provisioning first time, it creates an AzureAD object for that device and it doesnt allow you to delete untill you delete the hardware hash from Intune.
Is it neccessary to cleanup the AD object from Azure AD before starting the restaging process ? As per the document, I have to cleanup the device from Intune, Azure AD and reupload the hash for it to be successful.
How can we handle such scenarios where you have to restage the pc’s ?
I you like the restage a device without hardware modifications (motherboard replacement etc) the hash will be the same and you don’t need to cleanup the hash or AD object, the AD object will be reused and in Intune it will create a new Intune device object. The old Intune object will be stale and if you have automatic cleanup turned on after 90 days it will be deleted from Intune.
Hi Oliver,
A small mistake I encountered:
# reset batch requests collection
$requests = @()
$batchCount = 0
should be outside the “if ($requests.count -gt 0){}”
If I run the delete script in a csv file where the first 20 devices do not exist, the script will never reset the batchCount because the requests count is 0.
Otherwise, great script! Learned to work with batch requests thanks to this.
Hello Oliver,
Thank you for the nice script.
Unfortunately i have an issue with it.
I can run the script without any error messages. It doesnt give me any devices i the re-check.
But it also doesnt give me any feedback and the device serial number dont get removed in MEM.
Do i need a comma or semicolon between the serial numbers in the CSV or only line by line?
Or do you have an other idea why this issue can occur?
This is the outcome i get:
PS C:\temp> .\Start-AutopilotCleanupCSV.ps1 -CsvFile “C:\temp\autopilot-devices.csv” -IntuneCleanup
Waiting 60 seconds to re-check if devices are deleted…
These devices couldn’t be deleted (if no device is listed, everything went well):
Thank you in advance.
Manuel Onz
Did you add your devices to the .csv row by row? The first row needs to have the columns where one row has “Device Serial Number”, this is what gets parsed by the Import-Csv cmdlet.
Hi,
Is there a more detailed set of instructions. Sorry completely new to this. I have the script downloaded. I ran Install-Module -Name WindowsAutoPilotIntune separately but what do I do with the SDK?
Thanks,
Roger
Hi,
you have to modify the line $CsvFile = “C:\temp\autopilot-devices.csv” in the script to point to your .csv and then run the script by executing .\Start-AutopilotCleanupCSV.ps1 in a PowerShell window.
best,
Oliver