Workspace ONE UEM Tag Automation

Last Updated 06/08/2026

Alexander Askin needs a beer. Or a coffee. Maybe a tea. His choice. I’ve never met the person, but back in the year 2019 he created and shared one of best automation scripts for Workspace ONE UEM EVER! I continue to use it to this day because as much as UEM has changed since then, Smart Groups are still dumb and the only way to make them Smart is build them using UEM Tags.

UEM Tags can be added to device records using Workspace ONE Intelligence Automations or via the UEM APIs. Thanks to Alexander’s script you can finally build OEM specific Smart Groups, which for Windows devices makes the world so much better.

Have an HP or Dell specific bios/driver/firmware that should only be run on a Latitude 9510? Great – now you have a Smart Group for that.

Need to set different policies for laptops vs desktops? Covered.

Want a report of hardware models for inventory purposes? Covered.

There are dozens of use cases for this script. I love this script!

The original version of the script is lost to the Internet (at least to my meager search skills) but it used basic auth to connect to UEM and that’s not cool anymore. Below is a re-post of the original script but modified to use oAuth instead. It’s also been tweaked a bit against UEM Console 2602 as some of the API’s have changed over the years.

Remember, the scripts purpose is quite simple:

The script grabs the Windows computer’s hardware vendor ID name and the chassis type then automatically assigns a Tag to the device record in the UEM based on the values you as the administrator assigns for your hardware vendors. Make sure you pay special attention to the scrit’s vendor lists and hardware types and add whatever you have in your environment to the list. Right now the script covers Dell, HP, Acer, Asus, ROG, Apple macOS, iPad and iPhone.

So I’m raising my coffee mug to toast Alexander. CHEERS!! THANK YOU! for writing and sharing this script which I have since modified to work with oAuth.

Oh and one more thing….

Omnissa made Smart Groups smarter! Finally!

Smart Groups can now be created directly using OEM & Model lookup without needed to manually tag devices first. For some scenarios this eliminates the need to use this script at all. Wahoo!

To use this feature in the Workspace ONE UEM Console Create a New Smart Group, choose Platform and Operating System > Windows Equals Any then choose OEM & Model and select the appropriate devices.

Here is the full script:

<#
.SYNOPSIS
  Workspace ONE Auto Tagging (V2 OAuth) – preserves V1 behavior, only swaps Basic auth for OAuth2 client_credentials.

.NOTES
  - Tagging logic/endpoints are kept identical to V1:
    - GET  /api/system/info
    - GET  /api/system/groups/search/?groupid=...
    - GET  /api/mdm/devices/search
    - GET  /api/mdm/tags/search/?name=...&organizationgroupid=...
    - POST /api/mdm/tags/addtag
    - GET  /api/mdm/tags/{tagId}/devices
    - POST /api/mdm/tags/{tagId}/adddevices   with BulkValues.Value = [ deviceId ]
  - Only authentication changes:
    - Obtain OAuth token from /connect/token (client_credentials)
    - Use Authorization: Bearer <token> instead of Basic <base64(user:pass)>
#>

#----------------------------------------------------------[Declarations]----------------------------------------------------------
$allow_tagging                    = $true
$allow_model_tag                  = $true
$allow_missing_tag_auto_creation  = $true

# === Environment (from your V2 attempt) ===
$WorkspaceONEServer               = "https://cnWXYZ.awmdm.com"
$OAuth2ClientId                   = "PUT_YOURS_HERE"
$OAuth2ClientSecret               = "PUT_YOURS_HERE"
$OAuth2TokenUrl                   = "https://na.uemauth.workspaceone.com/connect/token"
$WorkspaceONEAPIKey               = "PUT_YOURS_HERE"

# IMPORTANT: V1 used the OG "GroupIdName" (e.g., dwpgWXYZ). To preserve V1 behavior, keep that here.
# If you truly want to use the OG display name instead, set $WorkspaceONEOrganizationGroupName to the name
# and the script will try both search methods (groupid then groupname).
$WorkspaceONEOrganizationGroupName = "PUT_YOURS_HERE"

$URL = $WorkspaceONEServer + "/api"

# Define Vendors and their relations to HW-Types (unchanged)
$vendors = @{}
$vendors.Add("Latitude*", "Dell")
$vendors.Add("Optiplex*", "Dell")
$vendors.Add("Precision*", "Dell")
$vendors.Add("Proliant*", "HP")
$vendors.Add("HP Z*", "HP")
$vendors.Add("HP Elite*", "HP")
$vendors.Add("Surface Pro*", "Microsoft")
$vendors.Add("Inspiron*", "Dell")
$vendors.Add("Acer*", "Acer")
$vendors.Add("Asus*", "Asus")
$vendors.Add("ROG*" , "Asus")
$vendors.Add("Mac*" , "Apple")
$vendors.Add("iPad*" , "Apple")
$vendors.Add("iPhone*" , "Apple")

# Define Chassis and their relations to HW-Types (unchanged)
$devicetypes = @{}
$devicetypes.Add("Optiplex*", "Desktop")
$devicetypes.Add("Precision T*", "Desktop")
$devicetypes.Add("Precision 7*", "Laptop")
$devicetypes.Add("Inspiron*" , "Desktop")
$devicetypes.Add("VMware*", "Desktop")
$devicetypes.Add("Virtual Machine", "Desktop")
$devicetypes.Add("Proliant*", "Laptop")
$devicetypes.Add("Precision M*", "Laptop")
$devicetypes.Add("Latitude*", "Laptop")
$devicetypes.Add("HP Z4*", "Desktop")
$devicetypes.Add("HP EliteBook*", "Laptop")
$devicetypes.Add("HP EliteDesk*", "Desktop")
$devicetypes.Add("HP Elite X*", "2-in-1")
$devicetypes.Add("Surface Pro 4*", "2-in-1")
$devicetypes.Add("Swift*" , "Laptop")
$devicetypes.Add("Asus*" , "Laptop")
$devicetypes.Add("ROG*" , "Laptop")

#-----------------------------------------------------------[OAuth]------------------------------------------------------------
Function Get-OAuth2AccessToken {
    $body = @{
        grant_type    = "client_credentials"
        client_id     = $OAuth2ClientId
        client_secret = $OAuth2ClientSecret
    }
    $headers = @{ "Content-Type" = "application/x-www-form-urlencoded" }

    try {
        $resp = Invoke-RestMethod -Method Post -Uri $OAuth2TokenUrl -Body $body -Headers $headers -ErrorAction Stop
        if (-not $resp.access_token) { throw "Token endpoint returned no access_token." }
        return $resp.access_token
    } catch {
        Write-Host "Error obtaining access token: $($_.Exception.Message)" -ForegroundColor Red
        if ($_.Exception.Response) {
            $r = New-Object IO.StreamReader ($_.Exception.Response.GetResponseStream())
            Write-Host ("Response Content: " + $r.ReadToEnd()) -ForegroundColor Yellow
            $r.Close()
        }
        exit 1
    }
}

$accessToken = Get-OAuth2AccessToken

# Construct REST HEADER (kept aligned with V1 except Authorization)
$header = @{
    "Authorization"  = "Bearer $accessToken"
    "aw-tenant-code" = $WorkspaceONEAPIKey
    "Accept"         = "application/json"
    "Content-Type"   = "application/json;version=2"
}

#-----------------------------------------------------------[Functions]------------------------------------------------------------

Function Check-ConsoleVersion {
    $endpointURL = $URL + "/system/info"
    $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header
    $ProductVersion = $webReturn.ProductVersion
    Write-Host("Console Version: ") -ForegroundColor Cyan -NoNewline
    Write-Host $ProductVersion
}

# V1 behavior was "search/?groupid=<GroupIdName>"
# To keep this functional even if you accidentally pass a display name, we try:
#  1) groupid=... (V1)
#  2) groupname=... (fallback)
Function Get-OrgGroupIdbyName ($WorkspaceONEOrgGroupName) {
    # Try V1 method first
    Write-Host("Getting Group ID with Name: " + $WorkspaceONEOrgGroupName) -NoNewline -ForegroundColor Cyan

    $endpointURL = $URL + "/system/groups/search/?groupid=" + [System.Uri]::EscapeDataString($WorkspaceONEOrgGroupName)
    try {
        $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header -ErrorAction Stop
        if ($webReturn -and $webReturn.LocationGroups -and $webReturn.LocationGroups.Id -and $webReturn.LocationGroups.Id.Value) {
            Write-Host(" - found: " + $webReturn.LocationGroups.Id.Value)
            return $webReturn
        }
    } catch {
        # fall through to groupname
    }

    # Fallback: groupname
    $endpointURL2 = $URL + "/system/groups/search/?groupname=" + [System.Uri]::EscapeDataString($WorkspaceONEOrgGroupName)
    $webReturn2 = Invoke-RestMethod -Method Get -Uri $endpointURL2 -Headers $header
    # Try to find exact match; otherwise take first
    $match = $null
    if ($webReturn2.OrganizationGroups) {
        $match = @($webReturn2.OrganizationGroups) | Where-Object { $_.Name -eq $WorkspaceONEOrgGroupName } | Select-Object -First 1
        if (-not $match) { $match = @($webReturn2.OrganizationGroups) | Select-Object -First 1 }
    }
    if ($match -and $match.Id) {
        Write-Host(" - found: " + $match.Id)
        return [pscustomobject]@{ LocationGroups = [pscustomobject]@{ Id = [pscustomobject]@{ Value = $match.Id } } }
    }

    Write-Host(" - not found") -ForegroundColor Red
    throw "Could not resolve Organization Group ID for '$WorkspaceONEOrgGroupName'."
}

Function Get-Devices {
    Write-Host("Getting all Devices") -NoNewline -ForegroundColor Cyan
    $endpointURL = $URL + "/mdm/devices/search"
    $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header
    Write-Host(" - found: " + $webReturn.Total)
    return $webReturn
}

Function Get-DeviceByID($WorkSpaceONEDeviceID) {
    Write-Host("Getting specific Device with ID: " + $WorkSpaceONEDeviceID)
    $endpointURL = $URL + "/mdm/devices/" + $WorkSpaceONEDeviceID
    $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header
    return $webReturn
}

Function Create-Tag($WorkSpaceONETagName) {
    $endpointURL = $URL + "/mdm/tags/addtag"
    $body = [pscustomobject]@{
        'TagName'        = $WorkSpaceONETagName
        'TagAvatar'      = $WorkSpaceONETagName
        'TagType'        = 1
        'LocationGroupId'= $global:WorkSpaceONEGroupID
    }
    $json = $body | ConvertTo-Json
    $webReturn = Invoke-RestMethod -Method Post -Uri $endpointURL -Headers $header -Body $json
    return $webReturn
}

Function Get-TagIDByName($WorkSpaceONETagName) {
    Write-Host("Getting TagID by Name """ + $WorkSpaceONETagName + """") -NoNewline -ForegroundColor Cyan
    $endpointURL = $URL + "/mdm/tags/search/?name=" + [System.Uri]::EscapeDataString($WorkSpaceONETagName) + "&organizationgroupid=" + $WorkSpaceONEGroupID
    $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header
    if($webReturn.Tags.Count -ge 1){
        Write-Host(" - found: " + $webReturn.Tags.Item(0).Id.Value + " (" + $webReturn.Tags.Item(0).TagName + ")")
        return $webReturn.Tags.Item(0).Id.Value
    } else {
        if($allow_missing_tag_auto_creation){
            $return = (Create-Tag -WorkSpaceONETagName $WorkSpaceONETagName).Value
            Write-Host(" - created: " + $return) -ForegroundColor Green
            return $return
        } else {
            Write-Host(" - not found -> please create this TAG manually in Workspace ONE Console") -ForegroundColor Yellow
        }
    }
}

Function Check-TagAlreadyOnDevice($WorkSpaceONETagID, $WorkSpaceONEDeviceID) {
    $endpointURL = $URL + "/mdm/tags/" + $WorkSpaceONETagID + "/devices"
    $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header
    $webReturn.Device.ForEach({
        $taggeddevice = $_
        if($taggeddevice.DeviceId -eq $WorkSpaceONEDeviceID) {
            return $true
        }
    })
    return $false
}

Function Add-TagToDevice($WorkSpaceONETagID, $WorkSpaceONEDeviceID) {
    if($allow_tagging -eq $true){
        $endpointURL = $URL + "/mdm/tags/" + $WorkSpaceONETagID + "/adddevices"
        $DeviceToAdd = @()
        $DeviceToAdd += $WorkSpaceONEDeviceID.ToString()
        $Values = [pscustomobject]@{
            'Value' = $DeviceToAdd
        }
        $body = [pscustomobject]@{
            'BulkValues' = $Values
        }
        $json = $body | ConvertTo-Json
        $webReturn = Invoke-RestMethod -Method Post -Uri $endpointURL -Headers $header -Body $json
        Write-Host("Returns: " + $webReturn)
    } else {
        Write-Host("-NO CHANGE-") -ForegroundColor Yellow -NoNewline
    }
}

#-----------------------------------------------------------[Execution]------------------------------------------------------------

Write-Host "Calling URL:" $endpointURL
$header.GetEnumerator() | ForEach-Object { Write-Host $_.Key ":" $_.Value }

if($allow_tagging -eq $false){
    Write-Host ("No Changes will be written as allow_tagging is set to $false") -ForegroundColor Yellow
}

Check-ConsoleVersion
$global:WorkSpaceONEGroupID = (Get-OrgGroupIdbyName($WorkspaceONEOrganizationGroupName)).LocationGroups.Id.Value

$WorkSpaceONEDeviceID = ""
$WorkSpaceONETagID = @{}

foreach($tag in $vendors.Values){
    if($WorkSpaceONETagID.ContainsKey($tag) -eq $false){
        $WorkSpaceONETagID.Add($tag, (Get-TagIDByName($tag)))
    }
}
foreach($tag in $devicetypes.Values){
    if($WorkSpaceONETagID.ContainsKey($tag) -eq $false){
        $WorkSpaceONETagID.Add($tag, (Get-TagIDByName($tag)))
    }
}

if($allow_model_tag){
    (Get-Devices).Devices.ForEach({
        $device = $_
        if(!$WorkSpaceONETagID[$device.Model] -and $device.Platform -eq "WinRT"){
            $WorkSpaceONETagID[$device.Model] = (Get-TagIDByName $device.Model)
        }
    })
}

Write-Host "-------------- Start processing ... ------------------"
(Get-Devices).Devices.ForEach({
    $device = $_
    $device_was_found = $false
    $WorkSpaceONEDeviceID = $device.Id.Value
    Write-Host($device.DeviceFriendlyName + "(ID:" + $device.Id.Value + ") is " + $device.Model) -NoNewline -ForegroundColor Yellow

    # Model Tagging - ONLY for WinRT devices (unchanged)
    if($allow_model_tag -and $WorkSpaceONETagID[$device.Model] -and $device.Platform -eq "WinRT"){
        If(Check-TagAlreadyOnDevice $WorkSpaceONETagID[$device.Model] $WorkSpaceONEDeviceID){
            Write-Host(" already tagged as """ + $device.Model + """ - SKIPPED, ") -ForegroundColor Green -NoNewline
        } else {
            Write-Host(" untagged Model - attempt to TAG it, ") -ForegroundColor Cyan -NoNewline
            Add-TagToDevice $WorkSpaceONETagID[$device.Model] $WorkSpaceONEDeviceID
        }
    }

    # Vendor Check (unchanged)
    if($vendors){
        foreach($vendor in $vendors.Keys){
            if ($device.Model -like $vendor){
                $device_was_found = $true
                if($WorkSpaceONETagID[$vendors[$vendor]]){
                    If(Check-TagAlreadyOnDevice $WorkSpaceONETagID[$vendors[$vendor]] $WorkSpaceONEDeviceID){
                        Write-Host(" already tagged as " + $vendors[$vendor] + " - SKIPPED, ") -ForegroundColor Green -NoNewline
                    } else {
                        Write-Host(" untagged """ + $vendors[$vendor] + """ - attempt to TAG it, ") -ForegroundColor Cyan -NoNewline
                        Add-TagToDevice $WorkSpaceONETagID[$vendors[$vendor]] $WorkSpaceONEDeviceID
                    }
                } else {
                    Write-Host("- TAG does not exist") -ForegroundColor Red
                }
            }
        }
        if($device_was_found -eq $false){
            Write-Host (" no Vendor entry found - SKIPPED, ") -ForegroundColor Gray -NoNewline
        }
    }

    $device_was_found = $false

    # Chassis / DeviceType Check (unchanged)
    if($devicetypes){
        foreach($devicetype in $devicetypes.Keys){
            if ($device.Model -like $devicetype){
                $device_was_found = $true
                if($WorkSpaceONETagID[$devicetypes[$devicetype]]){
                    If(Check-TagAlreadyOnDevice $WorkSpaceONETagID[$devicetypes[$devicetype]] $WorkSpaceONEDeviceID){
                        Write-Host(" already tagged as " + $devicetypes[$devicetype] + " - SKIPPED") -ForegroundColor Green
                    } else {
                        Write-Host(" untagged """ + $devicetypes[$devicetype] + """ - attempt to TAG it") -ForegroundColor Cyan
                        Add-TagToDevice $WorkSpaceONETagID[$devicetypes[$devicetype]] $WorkSpaceONEDeviceID
                    }
                } else {
                    Write-Host("- TAG does not exist") -ForegroundColor Red
                }
            }
        }
        if($device_was_found -eq $false){
            Write-Host (" no DeviceType entry found - SKIPPED") -ForegroundColor Gray
        }
    }
})

Leave a Reply