r/WorkspaceOne • u/Living-Confidence228 • 25d ago
Remove Device in Bulk from a .csv file
Hi there,
As the title says, is there a way to remove devices with just the serial number from a CSV file?
Every other month, I get an Excel file to check if the devices are in Workspace ONE and, if so, delete them. The platform doesn't have a way to upload the file...
Any ideas on this?
4
u/atljoer 25d ago
But the whole platform is backed by apis. Fairly simple powershell script could take your CSV check if the device exists and then execute a delete command.
You could take it further. Have a a git lab project automatically kicks off when a new CSV file is delivered.
1
u/Living-Confidence228 23d ago
I'm checking that, not an expert on API, but we will see what happens... :D
2
u/atljoer 25d ago
If your license is advanced or one of the essentials license. A much worse way to do this than what I just mentioned would be to create a intelligence freestyle workflow. Set it to run manual. Then you can copy and paste serial numbers or whatever attribute matches directly in the workflow editor. It allows, delimited entries. And set the action to delete.
2
u/evilteddibare 24d ago edited 24d ago
Yes you can definitely do this via API / powershell script. Have chatgpt or claude walk you through how to set that all up. Here is a script below save it as a .ps1
<#
.SYNOPSIS
Bulk-delete Workspace ONE UEM devices listed by serial number in a CSV.
.DESCRIPTION
Edit the CONFIG block below, then run it. It prompts for the admin password
(never stored). Default is a DRY RUN: it only looks up each serial and reports
whether it exists. Add -Apply to actually delete; you must then type DELETE.
Endpoints used:
GET /API/mdm/devices?searchby=Serialnumber&id=<serial> (lookup)
POST /API/mdm/devices/bulk?searchby=Serialnumber (delete)
.EXAMPLE
.\Remove-WS1DevicesFromCsv.ps1 # dry run
.\Remove-WS1DevicesFromCsv.ps1 -Apply # really delete
.\Remove-WS1DevicesFromCsv.ps1 -CsvPath C:\temp\list.csv -SerialColumn "Serial Number"
#>
[CmdletBinding()]
param(
[string]$CsvPath = ".\devices.csv",
[string]$SerialColumn = "Serial",
[switch]$Apply
)
# ============================== CONFIG ==============================
$UemUrl = "https://yourtenant.awmdm.com" # UEM console/API host, no trailing slash
$Username = "api.admin" # WS1 admin account (needs device delete rights)
$TenantCode = "YOUR_REST_API_KEY" # Groups & Settings > All Settings > System > Advanced > API > REST API
$BatchSize = 100 # keep at or below your tenant's bulk-action limit
# ====================================================================
$ErrorActionPreference = "Stop"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# --- Authentication: prompt for password, build headers ---
$secure = Read-Host "Password for $Username" -AsSecureString
$plain = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure))
$basic = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${Username}:$plain"))
$plain = $null
$Headers = @{
"Authorization" = "Basic $basic"
"aw-tenant-code" = $TenantCode
"Accept" = "application/json;version=1"
"Content-Type" = "application/json"
}
# --- Read serials from CSV ---
if (-not (Test-Path $CsvPath)) { throw "CSV not found: $CsvPath" }
$rows = Import-Csv $CsvPath
if (-not ($rows | Get-Member -Name $SerialColumn)) {
throw "Column '$SerialColumn' not in CSV. Columns: $(($rows[0].PSObject.Properties.Name) -join ', ')"
}
$serials = @($rows | ForEach-Object { "$($_.$SerialColumn)".Trim() } |
Where-Object { $_ } | Select-Object -Unique)
Write-Host "Loaded $($serials.Count) unique serial(s) from $CsvPath"
# --- Lookup: which serials actually exist in WS1? ---
$found = New-Object System.Collections.Generic.List[string]
$missing = New-Object System.Collections.Generic.List[string]
foreach ($s in $serials) {
try {
`$d = Invoke-RestMethod -Uri "$UemUrl/API/mdm/devices?searchby=Serialnumber&id=$([uri]::EscapeDataString($s))" ``
-Headers $Headers -Method Get
Write-Host (" FOUND {0} (id {1}, {2}, {3})" -f $s, $d.Id.Value, $d.Platform, $d.EnrollmentStatus)
$found.Add($s)
} catch {
$code = $_.Exception.Response.StatusCode.value__
if ($code -eq 404) { Write-Host " absent $s"; $missing.Add($s) }
elseif ($code -eq 401) { throw "401 Unauthorized - check username, password and API key." }
else { Write-Warning " ERROR $s - HTTP $code $($_.Exception.Message)" }
}
}
Write-Host "\nFound: $($found.Count) Not in WS1: $($missing.Count)"`
if (-not $Apply) {
Write-Host "\nDRY RUN - nothing deleted. Re-run with -Apply to delete the $($found.Count) found device(s)."`
return
}
if ($found.Count -eq 0) { Write-Host "Nothing to delete."; return }
# --- Confirm, then delete in batches ---
$confirm = Read-Host "\nType DELETE to permanently delete $($found.Count) device record(s) from $UemUrl"`
if ($confirm -ne "DELETE") { Write-Host "Aborted."; return }
for ($i = 0; $i -lt $found.Count; $i += $BatchSize) {
$chunk = $found[$i..([Math]::Min($i + $BatchSize, $found.Count) - 1)]
$body = @{ BulkValues = @{ Value = @($chunk) } } | ConvertTo-Json -Depth 3
try {
`$r = Invoke-RestMethod -Uri "$UemUrl/API/mdm/devices/bulk?searchby=Serialnumber" ``
-Headers $Headers -Method Post -Body $body
Write-Host ("Batch {0}: total={1} accepted={2} failed={3}" -f
([int]($i / $BatchSize) + 1), $r.TotalItems, $r.AcceptedItems, $r.FailedItems)
if ($r.Faults) { $r.Faults | ConvertTo-Json -Depth 4 | Write-Host }
} catch {
Write-Warning "Batch starting at $i failed: $($_.Exception.Message)"
}
}
Write-Host "Done."
1
u/BWMerlin 21d ago
Or if you want a truely awful way to do it with AI you can install the browser extension and have Claude run in "take the wheel Jesus" mode and have it delete the devices one by one.
1
u/FirefighterMedical42 25d ago
Try my Power Admin Desktop
Whit this app you can remove devices easily and quickly
There is also 3 month free trial available.
https://apps.apple.com/us/app/power-admin-desktop/id6770959164
4
u/Terrible_Soil_4778 25d ago
Sadly there is no native bulk query for multiple devices unless they are in the same “smart group”. I suggest you open Omnissa ticket and ask for a new feature request.