Automated Stale Device Cleanup in Azure Active Directory using a runbook

As with cleaning up inactive guest users, inactive devices also pose several issues for organizations.

Microsoft recommends cleaning up stale devices after 90 days, but does not provide a service option or automation to do so.

Therefore, here’s another runbook you may run to just report on your inactive devices, or to automatically (and optionally periodically) clean up inactive devices in your environment when the removeInactiveDevices switch is supplied.

Managed identity

When run locally, interactive sign in is required. When running as a runbook in Azure automation, the Managed Identity of the automation account is leveraged. This requires you to set Device.ReadWrite.All or Device.Read.All permissions depending on if you want to script to do the cleanup as well.

Autopilot / on premises devices

Note that the script will log an error (and not attempt to delete the device) when a device is an autopilot record (not a real device) or when the device is synced from an on-premises active directory.

Disable vs Delete

The runbook also has a disable option, in which it will first disable a device and wait a configurable ($disableDurationInDays) period of time before actually deleting a device.

Reports

If you wish, you can also let the script mail you a report in CSV format. Add the Mail.Send graph permissions like you did with device permissions and give the MailFrom and MailTo parameters a value.

Download

Download get-AzureADInactiveDevices.ps1 from Gitlab

Disclaimer

As always, the script is provided as-is and should be reviewed and then used at your own risk.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

28 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
DanielF
DanielF
2 years ago

Thanks for this post. I’m working on the same problem at the moment and it was really helpful to see that you have mostly tackled the problem in the same way. I do like your clean code and think I will take a few tips from that for sure! A few different decisions I’ve taken: I use the Graph PowerShell SDK which makes authentication and Graph calls a little easier, in my opinion. I write to an Azure Table for reporting in PowerBI instead of the csv output. I’m currently working on performing a wipe and retire on Intune-managed devices… Read more »

AlexA
AlexA
3 months ago

Hi,
I have been looking for a script to clean up my environment, this script is excellent.

I have close to 1000 stale devices with their device hash in Autopilot, any suggestions on how to handle these? Can the script remove the hash from Autopilot? 

Phibi
Phibi
9 months ago

Hi – I have ran this and it is awesome. However when we use the disable option only some of them show as Disabled and others Failed. Some of the Failed ones actually have disabled, but the problem is it doesn’t seem to have been able to update the Extension attribute 6 with the timestamp. What happens to these devices will they never delete? Or do you know why it would fail for some on that extension attribute as it works well for other devices? There are also some that have failed and haven’t disabled and do not have the… Read more »

Last edited 9 months ago by Phibi
Timo Schreieck
Timo Schreieck
11 months ago

thanks for this. Is there a solution for large datasets rather than using a hybrid worker?
Getting 40 K devices into an array is already exceeding the memory limit of the runbook sandbox environment

Christian Schroeder
Christian Schroeder
2 years ago

Hi Jos,

thank you for your script and how-to. I assigned the device.readwrite.all permission to the managed identity but got every time a 403 access denied error code back during the device deletion. I added the managed identity to the cloud device administrator role and it worked as expected. Do you have a hint for me?

I found only this comment via google:
https://docs.microsoft.com/en-us/answers/questions/338784/graph-api-application-create-devices.html

Abaig
Abaig
1 year ago

I am not able to run this script on local. please advise.