Keeping a clean and healthy environment is important when managing a large number of devices in Workspace ONE UEM. Over time duplicate and stale device records, especially for Windows 10, can grow and grow. Without regular maintenance, you could potentially have thousands of records that are no longer valid and will affect the reporting of your deployments. This happens due to normal attrition of devices, re-imaging, and well as device resets. I took an internal script developed by our VMware IT system admins and modified it for external use so that you can easily clean up device records using WS1 REST API. The script is available on Github: Start-UEMMaintenance_Windows.ps1. To download it, click “Raw” then right click and “Save As”.
What the script do?
This script connects to your VMware Workspace ONE UEM environment and gets duplicates, stale records, or problematic devices (devices with invalid serials). Once you run any of the “get” actions, it will save the data to several csv files located under C:\UEM-Maintenance\[uem server]\Win_[today’s date]. If you run it again on the same day, it will search for valid CSV files first before reaching out again to the server. This is to improve speed, allow for editing of CSVs, and reduce load on the server. If it does not find a valid csv, it will go ahead and reach out to the server. Additionally, it asks for and stores the UEM credentials in an encrypted file (saved C:\UEM-Maintenance\ [uem server]\Logs\Creds.txt) with AES encryption. The key is saved C:\UEM-Maintenance\ [uem server]\Logs\AES.key. This allows the script to be run in an automated fashion by a service account or multiple users on the same internal server. However since the encryption key is on the same system, care must be taken on who can access the system this script is running from. This key can also be saved on a different location for improved security.
Using REST API with Workspace One involves two things: first, you need an API key. This essentially “gets you in the door” and is tied to the Organization Group. However, to actually do anything, such as reading or deleting devices you still need a Workspace One admin account. This can either be a basic account or a service account synced in from Active Directory. The minimum this account needs is REST API access as well as “delete’ device records. The simplest way to achieve it is the grant the account “Airwatch Administrator” access. However, this gives it way more access than it needs so best practice always is to create a least privileged access role. If you do set it to “Airwatch Administrator”, give it that role during the clean up and then change the account a “read only” role. This is to ensure no one external ever get the API key and admin credentials.
Additionally, as mentioned above when you run the script the first time (per environment) it will prompt you for those admin credentials and then save them encrypted to C:\UEM-Maintenance\ [uem server]\Logs\Creds.txt. The key to unlock and retrieve the creds is in the same directory so this isn’t a true foolproof encrypted solution. It adds a layer of security but you still need to protect the AES.key file. You can do this several ways: First, delete the file after each time this script is run. If you do this then you will have to input credentials each time you run the script. Second, you can run this on an internal server where only legitimate admins have access. Or you can modify the script to store and retrieve this file from a different fileshare on your network.
With that out of the way, let’s dig into each function and how they work.
This function first gets all Windows 10 devices and saved them to Win_AllDevices.csv. Next, it goes through each record and counts how many times each serial number appears. Then it makes a new list of all of the devices that have more than one occurrence of a serial number and saves it to Win_AllDuplicateDevices.csv. It loops though that list and sorts based on “Device Last Seen” and then by “Device ID” so that most recent device (meaning the “good” device) is excluded. All of these exclusions are sent to Win_DoNotDeleteList.csv. The rest are the true duplicates and should be deleted. This list is Win_DuplicateDevicesToBeDeleted.csv and will be used by the “Delete-Duplicates” function. You can look at these files as well as edit them for accuracy as well as to reduce the final deletion list. I.e. as a test you could delete all rows except one in the Win_DuplicateDevicesToBeDeleted.csv file so that only one device will be deleted. This is a good test to ensure your credentials work properly. Additionally, this can sometimes take a long time depending on how large of an environment it is. I’ve seen it take 45 min for 160k device environment as well as several hours for a 300k+ device environment. Give it enough time to complete.
This function optionally supports -FilterSerial which means it excludes devices with invalid serials as those may not be true duplicates. See below for more details on this.
This will delete all devices found in Win_DuplicateDevicesToBeDeleted.csv. If that file is not there, it will then run Get-Duplicates.
This first looks for an Win_AllDevices.csv file and will import it if found. Otherwise it will run the “Get-Duplicates” command and pull live from the server. Then after all devices are gathered, it will then sort and look for all devices not seen in the last 90 days (default). If you specify a different value in the “day” parameter, then you can adjust how far back you want to filter. After this it will save all stale devices to Win_StaleDevices.csv.
This will delete all stale devices found in Win_StaleDevices.csv. If that file is not there, it will then run Get-Stale.
This first looks for an Win_AllDevices.csv file and will import it if found. Otherwise it will run the “Get-Duplicates” command and pull live from the server. Then after all devices are gathered, it will then sort based on the $SerialFilter variable at the top of the script. At the time of this writing, I have it set to filter any of these values
'System Serial Number', 'To be filled by O.E.M.', 'Default string', '', '0', '1234567'
Devices can have invalid serials for a number of reasons and need to be looked at why the inventory is not working properly. This can sometimes indicate a bad or incomplete enrollment. This function will save all problematic devices found to Win_ProblematicDevices.csv
This will delete all problematic devices found in Win_ProblematicDevices.csv. If that file is not found, it will run Get-Problematic.
Specifies the server you are targeting in format myserver.awmdm.com
Mandatory parameter for the API key that is requred for the script to connect via REST API to your server. These keys are per OG and are found under All Settings > System > Advanced > API > REST API.
Mandatory parameter that specifies the action the script should take. Options are: ‘Get-Duplicates’, ‘Delete-Duplicates’, ‘Get-Stale’, ‘Delete-Stale’, ‘Get-Problematic’, ‘Delete-Problematic’.
Optional parameter that specifies how many days back the script should check for stale records. Default is 90 days.
Optional parameter that filters out serials that are improperly formatted or not yet populated. See the $SerialFilter variable below for the full list. Use this for duplicates only and not stale or problematic.
Get Duplicates, Filtering Serials
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Get-Duplicates -FilterSerial
Get Stale devices (default of 90 days)
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Get-Stale
Get Stale devices older than 120 days
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Get-Stale -Days 120
Get problematic devices
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Get-Problematic
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Delete-Duplicates -FilterSerial
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Delete-Stale
.\Start-UEMMaintenance_Windows.ps1 -server myserver.awmdm.com -ApiKey “zwhD99G6593LDO0D93A030139nZti0sur0Gg=” -Action Delete-Problematic
And that’s the script! I would love to hear feedback on how well this works in your environment as well as things I should add to improve it.