Deep Dive: Workspace ONE UEM API Usage

Introduction: Unlocking the Power of Workspace ONE APIs

The Workspace ONE UEM API represents one of the most powerful yet underutilized capabilities in the platform. After spending years implementing API-driven solutions across hundreds of organizations, I can tell you that mastering the Workspace ONE API is what separates basic administrators from true automation architects who can solve complex business challenges that are impossible through the console alone.

If you’re a Workspace ONE administrator looking to go beyond manual console operations and implement sophisticated automation, custom integrations, and intelligent workflows, this comprehensive guide will show you how to effectively leverage the Workspace ONE UEM API. We’ll cover everything from basic authentication to advanced automation scenarios that will revolutionize how you manage your mobile and desktop environment.

Workspace ONE UEM API Integration

Understanding the Workspace ONE UEM API

What is the Workspace ONE UEM API?

The Workspace ONE UEM API is a comprehensive RESTful API that provides programmatic access to virtually all Workspace ONE UEM functionality. This API enables you to automate device management tasks, integrate with external systems, and create custom solutions that extend the platform’s capabilities far beyond what’s possible through the web console.

Key Capabilities of the UEM API:

  • Device Management: Programmatically manage devices, applications, and configurations
  • User Management: Automate user provisioning, group assignments, and access control
  • Content Management: Manage applications, profiles, and content distribution
  • Reporting and Analytics: Extract detailed reports and analytics data
  • System Administration: Automate administrative tasks and system configuration

Why API Integration Matters in Modern Device Management

In my experience, API integration addresses several critical limitations of manual console-based management:

Scale and Efficiency:

  • Bulk Operations: Perform operations on thousands of devices simultaneously
  • Automated Workflows: Create intelligent workflows that respond to events and conditions
  • Consistent Execution: Eliminate human error through automated processes
  • Time Savings: Reduce manual effort from hours to minutes for complex operations

Integration Capabilities:

  • External System Integration: Connect Workspace ONE with ITSM, CMDB, and other enterprise systems
  • Custom Applications: Build custom applications and portals for specific business needs
  • Data Synchronization: Keep data synchronized between Workspace ONE and other systems
  • Business Process Automation: Integrate device management into broader business processes

API Architecture and Structure

The Workspace ONE UEM API follows RESTful principles and is organized into logical functional areas.

API Structure Overview:

  1. Base URL Structure:
    • SaaS: https://[your-server].awmdm.com/API
    • On-Premises: https://[your-server]/API
    • Version-specific endpoints: /v1/, /v2/, etc.
  2. Authentication Methods:
    • Basic Authentication: Username and password for interactive scenarios
    • API Key Authentication: API key and tenant code for service accounts
    • OAuth 2.0: Modern authentication for third-party integrations
    • Certificate-Based: Certificate authentication for high-security scenarios

Core API Categories:

  1. Device Management APIs:
    • /devices – Device enrollment, management, and queries
    • /deviceprofiles – Device profile management and assignment
    • /devicecommands – Remote device commands and actions
    • /devicecompliance – Compliance monitoring and enforcement
  2. Application Management APIs:
    • /apps – Application catalog and deployment management
    • /appgroups – Application group management
    • /appinstalls – Application installation tracking and management
    • /appwrapping – Application wrapping and MAM policy management
  3. User and Group Management APIs:
    • /users – User account management and provisioning
    • /usergroups – User group management and assignments
    • /roles – Role-based access control management
    • /admins – Administrator account management
  4. Content and Configuration APIs:
    • /profiles – Configuration profile management
    • /resources – Resource and content management
    • /policies – Policy configuration and assignment
    • /certificates – Certificate management and distribution

Setting Up API Access

Configuring API Authentication

Before you can use the Workspace ONE UEM API, you need to configure appropriate authentication credentials.

Creating API Credentials in the Console:

  1. Access the API Configuration:
    • Navigate to Groups & SettingsAll Settings
    • Select SystemAdvancedAPI
    • Click on REST API to configure API settings
  2. Configure API Access:
    • Enable API Access for your organization group
    • Configure API Timeout settings (recommended: 300 seconds)
    • Set API Rate Limiting if required for your environment
    • Configure API Logging for audit and troubleshooting purposes

Creating Service Account Credentials:

  1. Create a Dedicated Service Account:
    • Navigate to AccountsAdministratorsList View
    • Click AddAdd Admin
    • Create a dedicated service account (e.g., “API_Service_Account”)
    • Assign appropriate roles based on required API operations
  2. Generate API Key:
    • Navigate to Groups & SettingsAll Settings
    • Select SystemAdvancedAPIREST API
    • Click Add to create a new API key
    • Configure the API key with appropriate permissions and expiration

Testing API Connectivity

Before implementing complex automation, it’s essential to test basic API connectivity and authentication.

Basic API Test Using PowerShell:

# Basic Workspace ONE UEM API Test Script

# API Configuration
$apiServer = "your-server.awmdm.com"  # Replace with your server
$apiKey = "your-api-key"              # Replace with your API key
$tenantCode = "your-tenant-code"      # Replace with your tenant code
$username = "api-service-account"     # Replace with your service account
$password = "service-account-password" # Replace with service account password

# Function to create authentication headers
function Get-AuthHeaders {
    param(
        [string]$Username,
        [string]$Password,
        [string]$ApiKey,
        [string]$TenantCode
    )
    
    # Create basic authentication string
    $authString = "$Username`:$Password"
    $authBytes = [System.Text.Encoding]::UTF8.GetBytes($authString)
    $authBase64 = [System.Convert]::ToBase64String($authBytes)
    
    # Create headers
    $headers = @{
        "Authorization" = "Basic $authBase64"
        "aw-tenant-code" = $TenantCode
        "Accept" = "application/json"
        "Content-Type" = "application/json"
    }
    
    return $headers
}

# Function to test API connectivity
function Test-APIConnectivity {
    param(
        [string]$Server,
        [hashtable]$Headers
    )
    
    try {
        Write-Host "Testing API connectivity to $Server..."
        
        # Test basic connectivity with system info endpoint
        $uri = "https://$Server/API/v1/system/info"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 30
        
        Write-Host "✓ API connectivity successful" -ForegroundColor Green
        Write-Host "  Server Version: $($response.ProductVersion)" -ForegroundColor Cyan
        Write-Host "  Build Number: $($response.BuildNumber)" -ForegroundColor Cyan
        Write-Host "  Server Time: $($response.CurrentServerTime)" -ForegroundColor Cyan
        
        return $true
        
    } catch {
        Write-Host "✗ API connectivity failed: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

# Function to test device query
function Test-DeviceQuery {
    param(
        [string]$Server,
        [hashtable]$Headers
    )
    
    try {
        Write-Host "Testing device query..."
        
        # Query first 10 devices
        $uri = "https://$Server/API/v1/devices/search?pagesize=10"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 30
        
        Write-Host "✓ Device query successful" -ForegroundColor Green
        Write-Host "  Total Devices: $($response.Total)" -ForegroundColor Cyan
        Write-Host "  Devices Returned: $($response.Devices.Count)" -ForegroundColor Cyan
        
        if ($response.Devices.Count -gt 0) {
            Write-Host "  Sample Device: $($response.Devices[0].DeviceFriendlyName)" -ForegroundColor Cyan
        }
        
        return $true
        
    } catch {
        Write-Host "✗ Device query failed: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

# Main testing process
Write-Host "=== Workspace ONE UEM API Connectivity Test ===" -ForegroundColor Yellow

# Create authentication headers
$headers = Get-AuthHeaders -Username $username -Password $password -ApiKey $apiKey -TenantCode $tenantCode

# Test basic connectivity
$connectivityTest = Test-APIConnectivity -Server $apiServer -Headers $headers

if ($connectivityTest) {
    # Test device query
    Test-DeviceQuery -Server $apiServer -Headers $headers
} else {
    Write-Host "Skipping additional tests due to connectivity failure" -ForegroundColor Yellow
}

Write-Host "=== Test Complete ===" -ForegroundColor Yellow

Testing with Postman or Similar Tools:

  1. Configure Postman Collection:
    • Import the official Workspace ONE UEM API Postman collection
    • Configure environment variables for your server and credentials
    • Test basic endpoints like /system/info and /devices/search
    • Verify response formats and status codes
  2. Common Test Endpoints:
    • GET /API/v1/system/info – System information and version
    • GET /API/v1/devices/search – Device search and listing
    • GET /API/v1/users/search – User search and listing
    • GET /API/v1/apps/search – Application search and listing

Common API Use Cases and Examples

Device Management Automation

Device management is one of the most common use cases for the Workspace ONE UEM API.

Example 1: Bulk Device Operations

This PowerShell script demonstrates how to perform bulk operations on devices:

# Bulk Device Operations Script
# Performs various bulk operations on Workspace ONE managed devices

# API Configuration
$apiConfig = @{
    Server = "your-server.awmdm.com"
    ApiKey = "your-api-key"
    TenantCode = "your-tenant-code"
    Username = "api-service-account"
    Password = "service-account-password"
}

# Function to create authentication headers
function Get-AuthHeaders {
    param([hashtable]$Config)
    
    $authString = "$($Config.Username):$($Config.Password)"
    $authBytes = [System.Text.Encoding]::UTF8.GetBytes($authString)
    $authBase64 = [System.Convert]::ToBase64String($authBytes)
    
    return @{
        "Authorization" = "Basic $authBase64"
        "aw-tenant-code" = $Config.TenantCode
        "Accept" = "application/json"
        "Content-Type" = "application/json"
    }
}

# Function to get devices by criteria
function Get-DevicesByCriteria {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [string]$Platform = "",
        [string]$Model = "",
        [string]$Ownership = "",
        [int]$PageSize = 500
    )
    
    try {
        $uri = "https://$($Config.Server)/API/v1/devices/search?pagesize=$PageSize"
        
        # Add search criteria
        if ($Platform) { $uri += "&platform=$Platform" }
        if ($Model) { $uri += "&model=$Model" }
        if ($Ownership) { $uri += "&ownership=$Ownership" }
        
        Write-Host "Querying devices with criteria: Platform=$Platform, Model=$Model, Ownership=$Ownership"
        
        $allDevices = @()
        $page = 0
        
        do {
            $pageUri = "$uri&page=$page"
            $response = Invoke-RestMethod -Uri $pageUri -Method GET -Headers $Headers -TimeoutSec 60
            
            if ($response.Devices) {
                $allDevices += $response.Devices
                Write-Host "Retrieved $($response.Devices.Count) devices from page $page"
            }
            
            $page++
        } while ($response.Devices.Count -eq $PageSize)
        
        Write-Host "Total devices retrieved: $($allDevices.Count)"
        return $allDevices
        
    } catch {
        Write-Error "Failed to retrieve devices: $($_.Exception.Message)"
        return @()
    }
}

# Function to send command to multiple devices
function Send-BulkDeviceCommand {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [array]$DeviceIds,
        [string]$Command,
        [hashtable]$CommandParameters = @{}
    )
    
    try {
        Write-Host "Sending command '$Command' to $($DeviceIds.Count) devices..."
        
        $successCount = 0
        $failureCount = 0
        $results = @()
        
        foreach ($deviceId in $DeviceIds) {
            try {
                $uri = "https://$($Config.Server)/API/v1/devices/$deviceId/commands"
                
                $commandBody = @{
                    Command = $Command
                } + $CommandParameters
                
                $jsonBody = $commandBody | ConvertTo-Json -Depth 10
                
                $response = Invoke-RestMethod -Uri $uri -Method POST -Headers $Headers -Body $jsonBody -TimeoutSec 30
                
                $results += [PSCustomObject]@{
                    DeviceId = $deviceId
                    Status = "Success"
                    CommandId = $response.CommandUuid
                    Message = "Command sent successfully"
                }
                
                $successCount++
                
            } catch {
                $results += [PSCustomObject]@{
                    DeviceId = $deviceId
                    Status = "Failed"
                    CommandId = $null
                    Message = $_.Exception.Message
                }
                
                $failureCount++
            }
            
            # Add small delay to avoid rate limiting
            Start-Sleep -Milliseconds 100
        }
        
        Write-Host "Command execution completed: $successCount successful, $failureCount failed"
        return $results
        
    } catch {
        Write-Error "Failed to send bulk device command: $($_.Exception.Message)"
        return @()
    }
}

# Function to update device tags
function Update-DeviceTags {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [array]$DeviceIds,
        [array]$TagsToAdd = @(),
        [array]$TagsToRemove = @()
    )
    
    try {
        Write-Host "Updating tags for $($DeviceIds.Count) devices..."
        
        $successCount = 0
        $failureCount = 0
        $results = @()
        
        foreach ($deviceId in $DeviceIds) {
            try {
                # Get current device details
                $deviceUri = "https://$($Config.Server)/API/v1/devices/$deviceId"
                $device = Invoke-RestMethod -Uri $deviceUri -Method GET -Headers $Headers -TimeoutSec 30
                
                # Prepare tag update
                $currentTags = if ($device.Tags) { $device.Tags } else { @() }
                $updatedTags = $currentTags | Where-Object { $_ -notin $TagsToRemove }
                $updatedTags += $TagsToAdd | Where-Object { $_ -notin $updatedTags }
                
                # Update device tags
                $updateBody = @{
                    Tags = $updatedTags
                }
                
                $jsonBody = $updateBody | ConvertTo-Json -Depth 10
                $updateUri = "https://$($Config.Server)/API/v1/devices/$deviceId"
                
                Invoke-RestMethod -Uri $updateUri -Method PUT -Headers $Headers -Body $jsonBody -TimeoutSec 30
                
                $results += [PSCustomObject]@{
                    DeviceId = $deviceId
                    Status = "Success"
                    PreviousTags = $currentTags -join ", "
                    UpdatedTags = $updatedTags -join ", "
                    Message = "Tags updated successfully"
                }
                
                $successCount++
                
            } catch {
                $results += [PSCustomObject]@{
                    DeviceId = $deviceId
                    Status = "Failed"
                    PreviousTags = ""
                    UpdatedTags = ""
                    Message = $_.Exception.Message
                }
                
                $failureCount++
            }
            
            # Add small delay to avoid rate limiting
            Start-Sleep -Milliseconds 200
        }
        
        Write-Host "Tag update completed: $successCount successful, $failureCount failed"
        return $results
        
    } catch {
        Write-Error "Failed to update device tags: $($_.Exception.Message)"
        return @()
    }
}

# Main execution
Write-Host "=== Workspace ONE UEM Bulk Device Operations ===" -ForegroundColor Yellow

# Create authentication headers
$headers = Get-AuthHeaders -Config $apiConfig

# Example 1: Get all Windows devices
Write-Host "`n--- Example 1: Retrieving Windows Devices ---" -ForegroundColor Cyan
$windowsDevices = Get-DevicesByCriteria -Config $apiConfig -Headers $headers -Platform "WinRT"

if ($windowsDevices.Count -gt 0) {
    Write-Host "Found $($windowsDevices.Count) Windows devices"
    
    # Example 2: Send sync command to first 5 Windows devices
    Write-Host "`n--- Example 2: Sending Sync Command ---" -ForegroundColor Cyan
    $deviceIds = $windowsDevices[0..4] | ForEach-Object { $_.Id.Value }
    $syncResults = Send-BulkDeviceCommand -Config $apiConfig -Headers $headers -DeviceIds $deviceIds -Command "SyncDevice"
    
    # Display results
    $syncResults | Format-Table -AutoSize
    
    # Example 3: Add tags to devices
    Write-Host "`n--- Example 3: Adding Tags to Devices ---" -ForegroundColor Cyan
    $tagsToAdd = @("BulkManaged", "APIControlled")
    $tagResults = Update-DeviceTags -Config $apiConfig -Headers $headers -DeviceIds $deviceIds -TagsToAdd $tagsToAdd
    
    # Display results
    $tagResults | Format-Table -AutoSize
    
} else {
    Write-Host "No Windows devices found for bulk operations"
}

Write-Host "`n=== Bulk Operations Complete ===" -ForegroundColor Yellow

Example 2: Automated Device Compliance Monitoring

This script monitors device compliance and takes automated remediation actions:

# Automated Device Compliance Monitoring Script
# Monitors device compliance and performs automated remediation

# API Configuration
$apiConfig = @{
    Server = "your-server.awmdm.com"
    ApiKey = "your-api-key"
    TenantCode = "your-tenant-code"
    Username = "api-service-account"
    Password = "service-account-password"
}

# Function to create authentication headers
function Get-AuthHeaders {
    param([hashtable]$Config)
    
    $authString = "$($Config.Username):$($Config.Password)"
    $authBytes = [System.Text.Encoding]::UTF8.GetBytes($authString)
    $authBase64 = [System.Convert]::ToBase64String($authBytes)
    
    return @{
        "Authorization" = "Basic $authBase64"
        "aw-tenant-code" = $Config.TenantCode
        "Accept" = "application/json"
        "Content-Type" = "application/json"
    }
}

# Function to get non-compliant devices
function Get-NonCompliantDevices {
    param(
        [hashtable]$Config,
        [hashtable]$Headers
    )
    
    try {
        Write-Host "Retrieving non-compliant devices..."
        
        $uri = "https://$($Config.Server)/API/v1/devices/compliance/noncompliant"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 60
        
        if ($response.Devices) {
            Write-Host "Found $($response.Devices.Count) non-compliant devices"
            return $response.Devices
        } else {
            Write-Host "No non-compliant devices found"
            return @()
        }
        
    } catch {
        Write-Error "Failed to retrieve non-compliant devices: $($_.Exception.Message)"
        return @()
    }
}

# Function to get device compliance details
function Get-DeviceComplianceDetails {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [int]$DeviceId
    )
    
    try {
        $uri = "https://$($Config.Server)/API/v1/devices/$DeviceId/compliance"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 30
        
        return $response
        
    } catch {
        Write-Warning "Failed to get compliance details for device $DeviceId`: $($_.Exception.Message)"
        return $null
    }
}

# Function to perform automated remediation
function Invoke-ComplianceRemediation {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [object]$Device,
        [object]$ComplianceDetails
    )
    
    $remediationActions = @()
    
    try {
        Write-Host "Analyzing compliance issues for device: $($Device.DeviceFriendlyName)"
        
        # Check for common compliance issues and determine remediation actions
        if ($ComplianceDetails.ComplianceStatus) {
            foreach ($rule in $ComplianceDetails.ComplianceStatus.ComplianceRules) {
                if (-not $rule.IsCompliant) {
                    Write-Host "  Non-compliant rule: $($rule.RuleName)"
                    
                    switch ($rule.RuleName) {
                        "Device Encryption" {
                            $remediationActions += @{
                                Action = "SendCommand"
                                Command = "EnableDeviceEncryption"
                                Description = "Enable device encryption"
                            }
                        }
                        "Passcode Policy" {
                            $remediationActions += @{
                                Action = "SendCommand"
                                Command = "ClearPasscode"
                                Description = "Clear passcode to force policy compliance"
                            }
                        }
                        "OS Version" {
                            $remediationActions += @{
                                Action = "SendCommand"
                                Command = "InstallOSUpdate"
                                Description = "Install OS updates"
                            }
                        }
                        "Jailbreak/Root Detection" {
                            $remediationActions += @{
                                Action = "Quarantine"
                                Command = "EnterpriseWipe"
                                Description = "Quarantine compromised device"
                            }
                        }
                        default {
                            $remediationActions += @{
                                Action = "Sync"
                                Command = "SyncDevice"
                                Description = "Sync device to refresh compliance status"
                            }
                        }
                    }
                }
            }
        }
        
        # Execute remediation actions
        foreach ($action in $remediationActions) {
            try {
                Write-Host "  Executing remediation: $($action.Description)"
                
                $uri = "https://$($Config.Server)/API/v1/devices/$($Device.Id.Value)/commands"
                
                $commandBody = @{
                    Command = $action.Command
                }
                
                $jsonBody = $commandBody | ConvertTo-Json -Depth 10
                
                $response = Invoke-RestMethod -Uri $uri -Method POST -Headers $Headers -Body $jsonBody -TimeoutSec 30
                
                Write-Host "    ✓ Remediation command sent successfully: $($response.CommandUuid)"
                
            } catch {
                Write-Warning "    ✗ Failed to send remediation command: $($_.Exception.Message)"
            }
        }
        
        return $remediationActions.Count
        
    } catch {
        Write-Error "Failed to perform compliance remediation for device $($Device.Id.Value): $($_.Exception.Message)"
        return 0
    }
}

# Function to generate compliance report
function Generate-ComplianceReport {
    param(
        [array]$NonCompliantDevices,
        [hashtable]$RemediationResults
    )
    
    try {
        $reportPath = Join-Path $env:TEMP "ComplianceReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
        
        $reportData = @()
        
        foreach ($device in $NonCompliantDevices) {
            $remediationCount = if ($RemediationResults.ContainsKey($device.Id.Value)) {
                $RemediationResults[$device.Id.Value]
            } else {
                0
            }
            
            $reportData += [PSCustomObject]@{
                DeviceId = $device.Id.Value
                DeviceName = $device.DeviceFriendlyName
                Platform = $device.Platform
                Model = $device.Model
                OSVersion = $device.OperatingSystem
                LastSeen = $device.LastSeen
                ComplianceStatus = $device.ComplianceStatus
                RemediationActions = $remediationCount
                Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            }
        }
        
        $reportData | Export-Csv -Path $reportPath -NoTypeInformation
        
        Write-Host "Compliance report generated: $reportPath"
        return $reportPath
        
    } catch {
        Write-Error "Failed to generate compliance report: $($_.Exception.Message)"
        return $null
    }
}

# Main compliance monitoring process
Write-Host "=== Workspace ONE UEM Compliance Monitoring ===" -ForegroundColor Yellow

# Create authentication headers
$headers = Get-AuthHeaders -Config $apiConfig

# Get non-compliant devices
$nonCompliantDevices = Get-NonCompliantDevices -Config $apiConfig -Headers $headers

if ($nonCompliantDevices.Count -gt 0) {
    Write-Host "`nProcessing $($nonCompliantDevices.Count) non-compliant devices..." -ForegroundColor Cyan
    
    $remediationResults = @{}
    $totalRemediationActions = 0
    
    foreach ($device in $nonCompliantDevices) {
        # Get detailed compliance information
        $complianceDetails = Get-DeviceComplianceDetails -Config $apiConfig -Headers $headers -DeviceId $device.Id.Value
        
        if ($complianceDetails) {
            # Perform automated remediation
            $actionCount = Invoke-ComplianceRemediation -Config $apiConfig -Headers $headers -Device $device -ComplianceDetails $complianceDetails
            $remediationResults[$device.Id.Value] = $actionCount
            $totalRemediationActions += $actionCount
        }
        
        # Add delay to avoid rate limiting
        Start-Sleep -Milliseconds 500
    }
    
    # Generate compliance report
    Write-Host "`nGenerating compliance report..." -ForegroundColor Cyan
    $reportPath = Generate-ComplianceReport -NonCompliantDevices $nonCompliantDevices -RemediationResults $remediationResults
    
    # Summary
    Write-Host "`n=== Compliance Monitoring Summary ===" -ForegroundColor Yellow
    Write-Host "Non-compliant devices processed: $($nonCompliantDevices.Count)"
    Write-Host "Total remediation actions taken: $totalRemediationActions"
    Write-Host "Report generated: $reportPath"
    
} else {
    Write-Host "All devices are compliant - no remediation required" -ForegroundColor Green
}

Write-Host "`n=== Compliance Monitoring Complete ===" -ForegroundColor Yellow

User and Group Management

The API provides powerful capabilities for automating user and group management tasks.

Example 3: Automated User Provisioning

This script demonstrates automated user provisioning and group assignment:

# Automated User Provisioning Script
# Provisions users and manages group assignments through the API

# API Configuration
$apiConfig = @{
    Server = "your-server.awmdm.com"
    ApiKey = "your-api-key"
    TenantCode = "your-tenant-code"
    Username = "api-service-account"
    Password = "service-account-password"
}

# Function to create authentication headers
function Get-AuthHeaders {
    param([hashtable]$Config)
    
    $authString = "$($Config.Username):$($Config.Password)"
    $authBytes = [System.Text.Encoding]::UTF8.GetBytes($authString)
    $authBase64 = [System.Convert]::ToBase64String($authBytes)
    
    return @{
        "Authorization" = "Basic $authBase64"
        "aw-tenant-code" = $Config.TenantCode
        "Accept" = "application/json"
        "Content-Type" = "application/json"
    }
}

# Function to create or update user
function Set-WorkspaceONEUser {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [hashtable]$UserData
    )
    
    try {
        # Check if user already exists
        $searchUri = "https://$($Config.Server)/API/v1/users/search?username=$($UserData.Username)"
        
        try {
            $existingUser = Invoke-RestMethod -Uri $searchUri -Method GET -Headers $Headers -TimeoutSec 30
            
            if ($existingUser.Users -and $existingUser.Users.Count -gt 0) {
                # User exists, update
                $userId = $existingUser.Users[0].Id.Value
                $updateUri = "https://$($Config.Server)/API/v1/users/$userId"
                
                Write-Host "Updating existing user: $($UserData.Username)"
                
                $response = Invoke-RestMethod -Uri $updateUri -Method PUT -Headers $Headers -Body ($UserData | ConvertTo-Json -Depth 10) -TimeoutSec 30
                
                return @{
                    Action = "Updated"
                    UserId = $userId
                    Username = $UserData.Username
                    Success = $true
                }
            }
        } catch {
            # User doesn't exist or search failed, proceed with creation
        }
        
        # Create new user
        $createUri = "https://$($Config.Server)/API/v1/users"
        
        Write-Host "Creating new user: $($UserData.Username)"
        
        $response = Invoke-RestMethod -Uri $createUri -Method POST -Headers $Headers -Body ($UserData | ConvertTo-Json -Depth 10) -TimeoutSec 30
        
        return @{
            Action = "Created"
            UserId = $response.Value
            Username = $UserData.Username
            Success = $true
        }
        
    } catch {
        Write-Error "Failed to create/update user $($UserData.Username): $($_.Exception.Message)"
        return @{
            Action = "Failed"
            UserId = $null
            Username = $UserData.Username
            Success = $false
            Error = $_.Exception.Message
        }
    }
}

# Function to assign user to groups
function Add-UserToGroups {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [int]$UserId,
        [array]$GroupNames
    )
    
    $results = @()
    
    foreach ($groupName in $GroupNames) {
        try {
            # Find group by name
            $groupSearchUri = "https://$($Config.Server)/API/v1/usergroups/search?name=$groupName"
            $groupResponse = Invoke-RestMethod -Uri $groupSearchUri -Method GET -Headers $Headers -TimeoutSec 30
            
            if ($groupResponse.UserGroups -and $groupResponse.UserGroups.Count -gt 0) {
                $groupId = $groupResponse.UserGroups[0].Id.Value
                
                # Add user to group
                $addToGroupUri = "https://$($Config.Server)/API/v1/usergroups/$groupId/users"
                $addUserBody = @{
                    Users = @(@{ Id = @{ Value = $UserId } })
                }
                
                Invoke-RestMethod -Uri $addToGroupUri -Method POST -Headers $Headers -Body ($addUserBody | ConvertTo-Json -Depth 10) -TimeoutSec 30
                
                Write-Host "  ✓ Added user to group: $groupName"
                
                $results += @{
                    GroupName = $groupName
                    GroupId = $groupId
                    Success = $true
                }
                
            } else {
                Write-Warning "  ✗ Group not found: $groupName"
                
                $results += @{
                    GroupName = $groupName
                    GroupId = $null
                    Success = $false
                    Error = "Group not found"
                }
            }
            
        } catch {
            Write-Warning "  ✗ Failed to add user to group $groupName`: $($_.Exception.Message)"
            
            $results += @{
                GroupName = $groupName
                GroupId = $null
                Success = $false
                Error = $_.Exception.Message
            }
        }
    }
    
    return $results
}

# Function to process user data from CSV
function Import-UsersFromCSV {
    param(
        [string]$CSVPath,
        [hashtable]$Config,
        [hashtable]$Headers
    )
    
    try {
        if (-not (Test-Path $CSVPath)) {
            Write-Error "CSV file not found: $CSVPath"
            return
        }
        
        $userData = Import-Csv -Path $CSVPath
        Write-Host "Processing $($userData.Count) users from CSV file..."
        
        $results = @()
        
        foreach ($user in $userData) {
            Write-Host "`nProcessing user: $($user.Username)"
            
            # Prepare user data for API
            $userApiData = @{
                Username = $user.Username
                FirstName = $user.FirstName
                LastName = $user.LastName
                Email = $user.Email
                Password = if ($user.Password) { $user.Password } else { "TempPassword123!" }
                SecurityType = if ($user.SecurityType) { $user.SecurityType } else { "Directory" }
                Status = if ($user.Status) { $user.Status } else { "Active" }
            }
            
            # Add optional fields if present
            if ($user.Department) { $userApiData.Department = $user.Department }
            if ($user.Title) { $userApiData.Title = $user.Title }
            if ($user.PhoneNumber) { $userApiData.PhoneNumber = $user.PhoneNumber }
            if ($user.MobileNumber) { $userApiData.MobileNumber = $user.MobileNumber }
            
            # Create or update user
            $userResult = Set-WorkspaceONEUser -Config $Config -Headers $Headers -UserData $userApiData
            
            if ($userResult.Success) {
                # Assign to groups if specified
                if ($user.Groups) {
                    $groupNames = $user.Groups -split ";"
                    $groupResults = Add-UserToGroups -Config $Config -Headers $Headers -UserId $userResult.UserId -GroupNames $groupNames
                    $userResult.GroupAssignments = $groupResults
                }
            }
            
            $results += $userResult
            
            # Add delay to avoid rate limiting
            Start-Sleep -Milliseconds 200
        }
        
        return $results
        
    } catch {
        Write-Error "Failed to import users from CSV: $($_.Exception.Message)"
        return @()
    }
}

# Function to generate provisioning report
function Generate-ProvisioningReport {
    param(
        [array]$Results,
        [string]$OutputPath
    )
    
    try {
        $reportData = @()
        
        foreach ($result in $Results) {
            $groupStatus = if ($result.GroupAssignments) {
                ($result.GroupAssignments | Where-Object { $_.Success }).Count
            } else {
                0
            }
            
            $reportData += [PSCustomObject]@{
                Username = $result.Username
                Action = $result.Action
                Success = $result.Success
                UserId = $result.UserId
                GroupsAssigned = $groupStatus
                Error = if ($result.Error) { $result.Error } else { "" }
                Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            }
        }
        
        $reportData | Export-Csv -Path $OutputPath -NoTypeInformation
        
        Write-Host "Provisioning report generated: $OutputPath"
        return $OutputPath
        
    } catch {
        Write-Error "Failed to generate provisioning report: $($_.Exception.Message)"
        return $null
    }
}

# Main user provisioning process
Write-Host "=== Workspace ONE UEM User Provisioning ===" -ForegroundColor Yellow

# Create authentication headers
$headers = Get-AuthHeaders -Config $apiConfig

# Example CSV file path (create this file with user data)
$csvPath = Join-Path $env:TEMP "users_to_provision.csv"

# Create sample CSV file if it doesn't exist
if (-not (Test-Path $csvPath)) {
    Write-Host "Creating sample CSV file: $csvPath"
    
    $sampleData = @"
Username,FirstName,LastName,Email,Department,Title,Groups
john.doe,John,Doe,john.doe@company.com,IT,System Administrator,IT Admins;All Users
jane.smith,Jane,Smith,jane.smith@company.com,HR,HR Manager,HR Team;All Users
bob.johnson,Bob,Johnson,bob.johnson@company.com,Sales,Sales Rep,Sales Team;All Users
"@
    
    $sampleData | Out-File -FilePath $csvPath -Encoding UTF8
    Write-Host "Sample CSV file created. Please update with actual user data and run the script again."
    return
}

# Process users from CSV
Write-Host "`nImporting users from CSV file..." -ForegroundColor Cyan
$provisioningResults = Import-UsersFromCSV -CSVPath $csvPath -Config $apiConfig -Headers $headers

# Generate report
if ($provisioningResults.Count -gt 0) {
    $reportPath = Join-Path $env:TEMP "UserProvisioningReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    Generate-ProvisioningReport -Results $provisioningResults -OutputPath $reportPath
    
    # Summary
    $successCount = ($provisioningResults | Where-Object { $_.Success }).Count
    $failureCount = ($provisioningResults | Where-Object { -not $_.Success }).Count
    
    Write-Host "`n=== User Provisioning Summary ===" -ForegroundColor Yellow
    Write-Host "Total users processed: $($provisioningResults.Count)"
    Write-Host "Successful operations: $successCount"
    Write-Host "Failed operations: $failureCount"
    Write-Host "Report generated: $reportPath"
}

Write-Host "`n=== User Provisioning Complete ===" -ForegroundColor Yellow

Application Management

The API provides comprehensive capabilities for managing applications and their deployment.

Example 4: Automated Application Deployment

This script demonstrates automated application deployment and management:

# Automated Application Deployment Script
# Manages application deployment and assignment through the API

# API Configuration
$apiConfig = @{
    Server = "your-server.awmdm.com"
    ApiKey = "your-api-key"
    TenantCode = "your-tenant-code"
    Username = "api-service-account"
    Password = "service-account-password"
}

# Function to create authentication headers
function Get-AuthHeaders {
    param([hashtable]$Config)
    
    $authString = "$($Config.Username):$($Config.Password)"
    $authBytes = [System.Text.Encoding]::UTF8.GetBytes($authString)
    $authBase64 = [System.Convert]::ToBase64String($authBytes)
    
    return @{
        "Authorization" = "Basic $authBase64"
        "aw-tenant-code" = $Config.TenantCode
        "Accept" = "application/json"
        "Content-Type" = "application/json"
    }
}

# Function to get application by name
function Get-ApplicationByName {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [string]$ApplicationName
    )
    
    try {
        $uri = "https://$($Config.Server)/API/v1/apps/search?applicationname=$ApplicationName"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 30
        
        if ($response.Applications -and $response.Applications.Count -gt 0) {
            return $response.Applications[0]
        } else {
            return $null
        }
        
    } catch {
        Write-Error "Failed to search for application '$ApplicationName': $($_.Exception.Message)"
        return $null
    }
}

# Function to assign application to smart group
function Set-ApplicationAssignment {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [int]$ApplicationId,
        [string]$SmartGroupName,
        [string]$AssignmentType = "Auto"
    )
    
    try {
        # Find smart group by name
        $groupSearchUri = "https://$($Config.Server)/API/v1/smartgroups/search?name=$SmartGroupName"
        $groupResponse = Invoke-RestMethod -Uri $groupSearchUri -Method GET -Headers $Headers -TimeoutSec 30
        
        if (-not ($groupResponse.SmartGroups -and $groupResponse.SmartGroups.Count -gt 0)) {
            Write-Error "Smart group not found: $SmartGroupName"
            return $false
        }
        
        $smartGroupId = $groupResponse.SmartGroups[0].SmartGroupId
        
        # Create assignment
        $assignmentUri = "https://$($Config.Server)/API/v1/apps/$ApplicationId/assignments"
        
        $assignmentBody = @{
            SmartGroupIds = @($smartGroupId)
            DeploymentParameters = @{
                AssignmentType = $AssignmentType
                PushMode = "Auto"
                WhenToInstall = @{
                    DiskSpaceRequiredInKb = 0
                    DeviceConnectivity = "OnlineAndOffline"
                    RamRequiredInMb = 0
                }
                WhenToCallInstallComplete = @{
                    UseAdditionalCriteria = $false
                }
                InstallContext = "Device"
                EncodedFileSize = 0
            }
        }
        
        $jsonBody = $assignmentBody | ConvertTo-Json -Depth 10
        
        $response = Invoke-RestMethod -Uri $assignmentUri -Method POST -Headers $Headers -Body $jsonBody -TimeoutSec 30
        
        Write-Host "✓ Application assigned to smart group: $SmartGroupName"
        return $true
        
    } catch {
        Write-Error "Failed to assign application to smart group '$SmartGroupName': $($_.Exception.Message)"
        return $false
    }
}

# Function to monitor application installation status
function Get-ApplicationInstallationStatus {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [int]$ApplicationId,
        [string]$ApplicationName
    )
    
    try {
        $uri = "https://$($Config.Server)/API/v1/apps/$ApplicationId/devices"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 30
        
        if ($response.DeviceApps) {
            $totalDevices = $response.DeviceApps.Count
            $installedCount = ($response.DeviceApps | Where-Object { $_.Status -eq "Installed" }).Count
            $failedCount = ($response.DeviceApps | Where-Object { $_.Status -eq "Failed" }).Count
            $pendingCount = ($response.DeviceApps | Where-Object { $_.Status -eq "Pending" }).Count
            
            return @{
                ApplicationName = $ApplicationName
                TotalDevices = $totalDevices
                Installed = $installedCount
                Failed = $failedCount
                Pending = $pendingCount
                InstallationRate = if ($totalDevices -gt 0) { [math]::Round(($installedCount / $totalDevices) * 100, 2) } else { 0 }
            }
        } else {
            return @{
                ApplicationName = $ApplicationName
                TotalDevices = 0
                Installed = 0
                Failed = 0
                Pending = 0
                InstallationRate = 0
            }
        }
        
    } catch {
        Write-Error "Failed to get installation status for application '$ApplicationName': $($_.Exception.Message)"
        return $null
    }
}

# Function to retry failed installations
function Retry-FailedInstallations {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [int]$ApplicationId,
        [string]$ApplicationName
    )
    
    try {
        Write-Host "Retrying failed installations for: $ApplicationName"
        
        # Get devices with failed installations
        $uri = "https://$($Config.Server)/API/v1/apps/$ApplicationId/devices"
        $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $Headers -TimeoutSec 30
        
        $failedDevices = $response.DeviceApps | Where-Object { $_.Status -eq "Failed" }
        
        if ($failedDevices.Count -eq 0) {
            Write-Host "No failed installations found for: $ApplicationName"
            return 0
        }
        
        Write-Host "Found $($failedDevices.Count) devices with failed installations"
        
        $retryCount = 0
        
        foreach ($deviceApp in $failedDevices) {
            try {
                # Send install command to device
                $commandUri = "https://$($Config.Server)/API/v1/devices/$($deviceApp.DeviceId)/commands"
                
                $commandBody = @{
                    Command = "InstallApplication"
                    ApplicationId = $ApplicationId
                }
                
                $jsonBody = $commandBody | ConvertTo-Json -Depth 10
                
                Invoke-RestMethod -Uri $commandUri -Method POST -Headers $Headers -Body $jsonBody -TimeoutSec 30
                
                $retryCount++
                
            } catch {
                Write-Warning "Failed to retry installation on device $($deviceApp.DeviceId): $($_.Exception.Message)"
            }
            
            # Add delay to avoid rate limiting
            Start-Sleep -Milliseconds 100
        }
        
        Write-Host "Retry commands sent to $retryCount devices"
        return $retryCount
        
    } catch {
        Write-Error "Failed to retry failed installations for '$ApplicationName': $($_.Exception.Message)"
        return 0
    }
}

# Function to generate application deployment report
function Generate-DeploymentReport {
    param(
        [array]$DeploymentResults,
        [string]$OutputPath
    )
    
    try {
        $reportData = @()
        
        foreach ($result in $DeploymentResults) {
            $reportData += [PSCustomObject]@{
                ApplicationName = $result.ApplicationName
                ApplicationId = $result.ApplicationId
                SmartGroup = $result.SmartGroup
                AssignmentSuccess = $result.AssignmentSuccess
                TotalDevices = $result.InstallationStatus.TotalDevices
                InstalledDevices = $result.InstallationStatus.Installed
                FailedDevices = $result.InstallationStatus.Failed
                PendingDevices = $result.InstallationStatus.Pending
                InstallationRate = $result.InstallationStatus.InstallationRate
                RetriedDevices = $result.RetriedDevices
                Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            }
        }
        
        $reportData | Export-Csv -Path $OutputPath -NoTypeInformation
        
        Write-Host "Deployment report generated: $OutputPath"
        return $OutputPath
        
    } catch {
        Write-Error "Failed to generate deployment report: $($_.Exception.Message)"
        return $null
    }
}

# Main application deployment process
Write-Host "=== Workspace ONE UEM Application Deployment ===" -ForegroundColor Yellow

# Create authentication headers
$headers = Get-AuthHeaders -Config $apiConfig

# Define applications to deploy
$applicationsToDeploy = @(
    @{
        Name = "Microsoft Office 365"
        SmartGroup = "All Windows Devices"
        AssignmentType = "Auto"
    },
    @{
        Name = "Adobe Acrobat Reader DC"
        SmartGroup = "All Windows Devices"
        AssignmentType = "Auto"
    },
    @{
        Name = "Google Chrome"
        SmartGroup = "All Devices"
        AssignmentType = "Auto"
    }
)

$deploymentResults = @()

foreach ($appConfig in $applicationsToDeploy) {
    Write-Host "`n--- Processing Application: $($appConfig.Name) ---" -ForegroundColor Cyan
    
    # Find application
    $application = Get-ApplicationByName -Config $apiConfig -Headers $headers -ApplicationName $appConfig.Name
    
    if ($application) {
        Write-Host "Found application: $($application.ApplicationName) (ID: $($application.Id.Value))"
        
        # Assign to smart group
        $assignmentSuccess = Set-ApplicationAssignment -Config $apiConfig -Headers $headers -ApplicationId $application.Id.Value -SmartGroupName $appConfig.SmartGroup -AssignmentType $appConfig.AssignmentType
        
        # Wait for assignment to propagate
        Start-Sleep -Seconds 10
        
        # Get installation status
        $installationStatus = Get-ApplicationInstallationStatus -Config $apiConfig -Headers $headers -ApplicationId $application.Id.Value -ApplicationName $application.ApplicationName
        
        # Retry failed installations if any
        $retriedDevices = 0
        if ($installationStatus -and $installationStatus.Failed -gt 0) {
            $retriedDevices = Retry-FailedInstallations -Config $apiConfig -Headers $headers -ApplicationId $application.Id.Value -ApplicationName $application.ApplicationName
        }
        
        $deploymentResults += @{
            ApplicationName = $application.ApplicationName
            ApplicationId = $application.Id.Value
            SmartGroup = $appConfig.SmartGroup
            AssignmentSuccess = $assignmentSuccess
            InstallationStatus = $installationStatus
            RetriedDevices = $retriedDevices
        }
        
    } else {
        Write-Warning "Application not found: $($appConfig.Name)"
        
        $deploymentResults += @{
            ApplicationName = $appConfig.Name
            ApplicationId = $null
            SmartGroup = $appConfig.SmartGroup
            AssignmentSuccess = $false
            InstallationStatus = $null
            RetriedDevices = 0
        }
    }
}

# Generate deployment report
if ($deploymentResults.Count -gt 0) {
    Write-Host "`n--- Generating Deployment Report ---" -ForegroundColor Cyan
    
    $reportPath = Join-Path $env:TEMP "ApplicationDeploymentReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    Generate-DeploymentReport -DeploymentResults $deploymentResults -OutputPath $reportPath
    
    # Summary
    $successfulAssignments = ($deploymentResults | Where-Object { $_.AssignmentSuccess }).Count
    $totalApplications = $deploymentResults.Count
    
    Write-Host "`n=== Application Deployment Summary ===" -ForegroundColor Yellow
    Write-Host "Total applications processed: $totalApplications"
    Write-Host "Successful assignments: $successfulAssignments"
    Write-Host "Report generated: $reportPath"
    
    # Display installation status summary
    foreach ($result in $deploymentResults | Where-Object { $_.InstallationStatus }) {
        Write-Host "`n$($result.ApplicationName):"
        Write-Host "  Total devices: $($result.InstallationStatus.TotalDevices)"
        Write-Host "  Installed: $($result.InstallationStatus.Installed) ($($result.InstallationStatus.InstallationRate)%)"
        Write-Host "  Failed: $($result.InstallationStatus.Failed)"
        Write-Host "  Pending: $($result.InstallationStatus.Pending)"
        if ($result.RetriedDevices -gt 0) {
            Write-Host "  Retried: $($result.RetriedDevices)"
        }
    }
}

Write-Host "`n=== Application Deployment Complete ===" -ForegroundColor Yellow

Advanced API Integration Patterns

Error Handling and Resilience

Robust error handling is essential for production API integrations.

Comprehensive Error Handling Pattern:

# Advanced Error Handling and Resilience Pattern for Workspace ONE API

# Function for resilient API calls with retry logic
function Invoke-ResilientAPICall {
    param(
        [string]$Uri,
        [string]$Method = "GET",
        [hashtable]$Headers,
        [object]$Body = $null,
        [int]$MaxRetries = 3,
        [int]$BaseDelaySeconds = 2,
        [array]$RetryableStatusCodes = @(429, 500, 502, 503, 504),
        [int]$TimeoutSeconds = 30
    )
    
    for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) {
        try {
            $params = @{
                Uri = $Uri
                Method = $Method
                Headers = $Headers
                TimeoutSec = $TimeoutSeconds
                UseBasicParsing = $true
            }
            
            if ($Body) {
                if ($Body -is [string]) {
                    $params.Body = $Body
                } else {
                    $params.Body = $Body | ConvertTo-Json -Depth 10
                }
                $params.ContentType = "application/json"
            }
            
            $response = Invoke-RestMethod @params
            
            # Success - return response
            return @{
                Success = $true
                Data = $response
                Attempt = $attempt
                Error = $null
            }
            
        } catch {
            $statusCode = $null
            $errorMessage = $_.Exception.Message
            
            # Extract status code if available
            if ($_.Exception.Response) {
                $statusCode = [int]$_.Exception.Response.StatusCode
            }
            
            Write-Warning "API call attempt $attempt failed: $errorMessage (Status: $statusCode)"
            
            # Check if we should retry
            $shouldRetry = $false
            
            if ($attempt -lt $MaxRetries) {
                if ($statusCode -and $statusCode -in $RetryableStatusCodes) {
                    $shouldRetry = $true
                } elseif ($_.Exception -is [System.Net.WebException] -and $_.Exception.Status -eq "Timeout") {
                    $shouldRetry = $true
                } elseif ($_.Exception -is [System.Net.WebException] -and $_.Exception.Status -eq "ConnectFailure") {
                    $shouldRetry = $true
                }
            }
            
            if ($shouldRetry) {
                # Calculate delay with exponential backoff
                $delay = $BaseDelaySeconds * [Math]::Pow(2, $attempt - 1)
                Write-Host "Retrying in $delay seconds..."
                Start-Sleep -Seconds $delay
            } else {
                # No more retries or non-retryable error
                return @{
                    Success = $false
                    Data = $null
                    Attempt = $attempt
                    Error = @{
                        Message = $errorMessage
                        StatusCode = $statusCode
                        Exception = $_.Exception
                    }
                }
            }
        }
    }
    
    # All retries exhausted
    return @{
        Success = $false
        Data = $null
        Attempt = $MaxRetries
        Error = @{
            Message = "All retry attempts exhausted"
            StatusCode = $statusCode
            Exception = $_.Exception
        }
    }
}

# Function for batch processing with error handling
function Invoke-BatchAPIOperation {
    param(
        [array]$Items,
        [scriptblock]$Operation,
        [int]$BatchSize = 10,
        [int]$DelayBetweenBatches = 1000,
        [switch]$ContinueOnError
    )
    
    $results = @()
    $totalItems = $Items.Count
    $processedItems = 0
    
    Write-Host "Processing $totalItems items in batches of $BatchSize..."
    
    for ($i = 0; $i -lt $totalItems; $i += $BatchSize) {
        $batchEnd = [Math]::Min($i + $BatchSize - 1, $totalItems - 1)
        $batch = $Items[$i..$batchEnd]
        
        Write-Host "Processing batch $([Math]::Floor($i / $BatchSize) + 1): items $($i + 1) to $($batchEnd + 1)"
        
        foreach ($item in $batch) {
            try {
                $result = & $Operation $item
                $results += @{
                    Item = $item
                    Success = $true
                    Result = $result
                    Error = $null
                }
                
                $processedItems++
                
            } catch {
                $errorResult = @{
                    Item = $item
                    Success = $false
                    Result = $null
                    Error = $_.Exception.Message
                }
                
                $results += $errorResult
                
                if (-not $ContinueOnError) {
                    Write-Error "Batch processing failed on item: $item"
                    throw $_
                }
                
                Write-Warning "Error processing item $item`: $($_.Exception.Message)"
            }
        }
        
        # Progress update
        $percentComplete = [Math]::Round(($processedItems / $totalItems) * 100, 2)
        Write-Host "Progress: $processedItems/$totalItems ($percentComplete%)"
        
        # Delay between batches to avoid rate limiting
        if ($i + $BatchSize -lt $totalItems) {
            Start-Sleep -Milliseconds $DelayBetweenBatches
        }
    }
    
    return $results
}

# Function for monitoring long-running operations
function Wait-ForOperationCompletion {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [string]$OperationId,
        [string]$OperationType,
        [int]$MaxWaitMinutes = 30,
        [int]$CheckIntervalSeconds = 30
    )
    
    $startTime = Get-Date
    $maxWaitTime = $startTime.AddMinutes($MaxWaitMinutes)
    
    Write-Host "Monitoring $OperationType operation: $OperationId"
    Write-Host "Maximum wait time: $MaxWaitMinutes minutes"
    
    do {
        try {
            # Check operation status (this would be specific to the operation type)
            $statusUri = "https://$($Config.Server)/API/v1/operations/$OperationId/status"
            $statusResponse = Invoke-ResilientAPICall -Uri $statusUri -Method GET -Headers $Headers
            
            if ($statusResponse.Success) {
                $status = $statusResponse.Data.Status
                $progress = $statusResponse.Data.Progress
                
                Write-Host "Operation status: $status ($progress% complete)"
                
                if ($status -eq "Completed") {
                    Write-Host "✓ Operation completed successfully"
                    return @{
                        Success = $true
                        Status = $status
                        Result = $statusResponse.Data
                    }
                } elseif ($status -eq "Failed") {
                    Write-Error "✗ Operation failed"
                    return @{
                        Success = $false
                        Status = $status
                        Error = $statusResponse.Data.ErrorMessage
                    }
                }
            } else {
                Write-Warning "Failed to check operation status: $($statusResponse.Error.Message)"
            }
            
        } catch {
            Write-Warning "Error checking operation status: $($_.Exception.Message)"
        }
        
        # Wait before next check
        Start-Sleep -Seconds $CheckIntervalSeconds
        
    } while ((Get-Date) -lt $maxWaitTime)
    
    # Timeout reached
    Write-Warning "Operation monitoring timed out after $MaxWaitMinutes minutes"
    return @{
        Success = $false
        Status = "Timeout"
        Error = "Operation monitoring timed out"
    }
}

Performance Optimization

Optimizing API performance is crucial for large-scale operations.

Performance Optimization Techniques:

# Performance Optimization Techniques for Workspace ONE API

# Function for parallel API processing
function Invoke-ParallelAPIOperations {
    param(
        [array]$Operations,
        [int]$MaxConcurrency = 5,
        [int]$TimeoutMinutes = 10
    )
    
    $jobs = @()
    $results = @()
    
    Write-Host "Starting $($Operations.Count) operations with max concurrency: $MaxConcurrency"
    
    # Process operations in parallel batches
    for ($i = 0; $i -lt $Operations.Count; $i += $MaxConcurrency) {
        $batchEnd = [Math]::Min($i + $MaxConcurrency - 1, $Operations.Count - 1)
        $batch = $Operations[$i..$batchEnd]
        
        Write-Host "Processing parallel batch: operations $($i + 1) to $($batchEnd + 1)"
        
        # Start jobs for this batch
        foreach ($operation in $batch) {
            $job = Start-Job -ScriptBlock $operation.ScriptBlock -ArgumentList $operation.Arguments
            $jobs += @{
                Job = $job
                Operation = $operation
            }
        }
        
        # Wait for batch to complete
        $batchJobs = $jobs | Where-Object { $_.Job.State -eq "Running" }
        $timeout = (Get-Date).AddMinutes($TimeoutMinutes)
        
        do {
            Start-Sleep -Seconds 1
            $runningJobs = $batchJobs | Where-Object { $_.Job.State -eq "Running" }
        } while ($runningJobs.Count -gt 0 -and (Get-Date) -lt $timeout)
        
        # Collect results from completed jobs
        foreach ($jobInfo in $jobs) {
            try {
                if ($jobInfo.Job.State -eq "Completed") {
                    $result = Receive-Job -Job $jobInfo.Job
                    $results += @{
                        Operation = $jobInfo.Operation.Name
                        Success = $true
                        Result = $result
                        Error = $null
                    }
                } else {
                    $results += @{
                        Operation = $jobInfo.Operation.Name
                        Success = $false
                        Result = $null
                        Error = "Job failed or timed out (State: $($jobInfo.Job.State))"
                    }
                }
            } catch {
                $results += @{
                    Operation = $jobInfo.Operation.Name
                    Success = $false
                    Result = $null
                    Error = $_.Exception.Message
                }
            } finally {
                Remove-Job -Job $jobInfo.Job -Force
            }
        }
        
        # Clear jobs for next batch
        $jobs = @()
    }
    
    return $results
}

# Function for efficient data pagination
function Get-AllPaginatedData {
    param(
        [hashtable]$Config,
        [hashtable]$Headers,
        [string]$BaseUri,
        [int]$PageSize = 500,
        [string]$PageParameter = "page",
        [string]$PageSizeParameter = "pagesize"
    )
    
    $allData = @()
    $page = 0
    $hasMoreData = $true
    
    Write-Host "Retrieving paginated data from: $BaseUri"
    
    while ($hasMoreData) {
        try {
            # Construct URI with pagination parameters
            $separator = if ($BaseUri.Contains("?")) { "&" } else { "?" }
            $uri = "$BaseUri$separator$PageSizeParameter=$PageSize&$PageParameter=$page"
            
            $response = Invoke-ResilientAPICall -Uri $uri -Method GET -Headers $Headers
            
            if ($response.Success) {
                $data = $response.Data
                
                # Extract data based on common response patterns
                $pageData = $null
                if ($data.Devices) { $pageData = $data.Devices }
                elseif ($data.Users) { $pageData = $data.Users }
                elseif ($data.Applications) { $pageData = $data.Applications }
                elseif ($data.Items) { $pageData = $data.Items }
                elseif ($data -is [array]) { $pageData = $data }
                else { $pageData = @($data) }
                
                if ($pageData -and $pageData.Count -gt 0) {
                    $allData += $pageData
                    Write-Host "Retrieved page $page`: $($pageData.Count) items (Total: $($allData.Count))"
                    
                    # Check if we have more data
                    $hasMoreData = $pageData.Count -eq $PageSize
                    $page++
                } else {
                    $hasMoreData = $false
                }
            } else {
                Write-Error "Failed to retrieve page $page`: $($response.Error.Message)"
                $hasMoreData = $false
            }
            
        } catch {
            Write-Error "Error retrieving page $page`: $($_.Exception.Message)"
            $hasMoreData = $false
        }
    }
    
    Write-Host "Pagination complete. Total items retrieved: $($allData.Count)"
    return $allData
}

# Function for efficient bulk operations with rate limiting
function Invoke-BulkOperationWithRateLimit {
    param(
        [array]$Items,
        [scriptblock]$Operation,
        [int]$RequestsPerSecond = 10,
        [int]$BurstSize = 50,
        [switch]$ShowProgress
    )
    
    $results = @()
    $totalItems = $Items.Count
    $processedItems = 0
    $startTime = Get-Date
    
    # Calculate timing for rate limiting
    $intervalMs = 1000 / $RequestsPerSecond
    $burstInterval = $BurstSize * $intervalMs
    
    Write-Host "Processing $totalItems items with rate limit: $RequestsPerSecond requests/second"
    
    for ($i = 0; $i -lt $totalItems; $i++) {
        $item = $Items[$i]
        
        try {
            $operationStart = Get-Date
            $result = & $Operation $item
            $operationEnd = Get-Date
            
            $results += @{
                Item = $item
                Success = $true
                Result = $result
                Duration = ($operationEnd - $operationStart).TotalMilliseconds
                Error = $null
            }
            
            $processedItems++
            
        } catch {
            $results += @{
                Item = $item
                Success = $false
                Result = $null
                Duration = 0
                Error = $_.Exception.Message
            }
        }
        
        # Rate limiting
        if (($i + 1) % $BurstSize -eq 0) {
            # Burst complete, wait for burst interval
            Start-Sleep -Milliseconds $burstInterval
        } else {
            # Regular interval
            Start-Sleep -Milliseconds $intervalMs
        }
        
        # Progress reporting
        if ($ShowProgress -and ($processedItems % 100 -eq 0 -or $processedItems -eq $totalItems)) {
            $elapsed = (Get-Date) - $startTime
            $rate = $processedItems / $elapsed.TotalSeconds
            $eta = if ($rate -gt 0) { [TimeSpan]::FromSeconds(($totalItems - $processedItems) / $rate) } else { [TimeSpan]::Zero }
            
            Write-Host "Progress: $processedItems/$totalItems ($([Math]::Round(($processedItems / $totalItems) * 100, 1))%) - Rate: $([Math]::Round($rate, 1))/sec - ETA: $($eta.ToString('hh:mm:ss'))"
        }
    }
    
    $totalTime = (Get-Date) - $startTime
    $averageRate = $processedItems / $totalTime.TotalSeconds
    
    Write-Host "Bulk operation completed in $($totalTime.ToString('hh:mm:ss')) - Average rate: $([Math]::Round($averageRate, 1))/sec"
    
    return $results
}

Best Practices for API Integration

Security and Authentication

Security is paramount when working with the Workspace ONE UEM API.

Security Best Practices:

  1. Credential Management:
    • Store API credentials securely using Windows Credential Manager or Azure Key Vault
    • Use service accounts with minimal required permissions
    • Implement credential rotation policies
    • Never hardcode credentials in scripts or configuration files
  2. API Key Security:
    • Generate unique API keys for each integration or service
    • Set appropriate expiration dates for API keys
    • Monitor API key usage and detect anomalies
    • Revoke unused or compromised API keys immediately
  3. Network Security:
    • Use HTTPS for all API communications
    • Implement certificate pinning where possible
    • Restrict API access to specific IP addresses or networks
    • Use VPN or private network connections for sensitive operations

Performance and Scalability

Designing for performance ensures your API integrations can scale with your organization.

Performance Best Practices:

  1. Efficient API Usage:
    • Use appropriate page sizes for data retrieval (typically 500-1000 items)
    • Implement caching for frequently accessed data
    • Use bulk operations where available
    • Minimize unnecessary API calls through intelligent caching
  2. Rate Limiting Compliance:
    • Respect API rate limits and implement backoff strategies
    • Monitor API usage and adjust request patterns accordingly
    • Implement queue-based processing for high-volume operations
    • Use parallel processing judiciously to avoid overwhelming the API

Monitoring and Maintenance

Ongoing monitoring ensures your API integrations remain reliable and performant.

Monitoring Best Practices:

  1. Logging and Auditing:
    • Implement comprehensive logging for all API operations
    • Log both successful operations and errors with sufficient detail
    • Use structured logging formats for easier analysis
    • Implement log retention policies and archival strategies
  2. Health Monitoring:
    • Implement health checks for API connectivity and authentication
    • Monitor API response times and error rates
    • Set up alerts for API failures or performance degradation
    • Create dashboards for visualizing API usage and performance metrics

Conclusion: Mastering the Workspace ONE UEM API

The Workspace ONE UEM API represents the ultimate tool for achieving enterprise-scale device management automation. After implementing API-driven solutions across hundreds of organizations, I can confidently say that mastering this capability is what transforms good administrators into automation architects who can solve complex business challenges that are impossible through manual console operations.

Key Success Factors

Organizations that excel with the Workspace ONE UEM API share several characteristics:

  • Strategic Vision: They view the API as a strategic enabler, not just a technical tool
  • Security-First Approach: They implement robust security practices from the beginning
  • Iterative Development: They start with simple use cases and gradually build complexity
  • Comprehensive Testing: They thoroughly test API integrations before production deployment

Transformative Impact

The impact of mastering the Workspace ONE UEM API extends far beyond simple automation:

  • Operational Excellence: Achieve unprecedented levels of automation and consistency
  • Business Integration: Integrate device management into broader business processes
  • Innovation Enablement: Enable new use cases and business capabilities
  • Competitive Advantage: Deliver capabilities that differentiate your organization

Looking Forward

As enterprise mobility continues to evolve, the API will remain the critical bridge between Workspace ONE and the broader enterprise ecosystem. The key is to approach API integration strategically—building a foundation of secure, reliable, and maintainable integrations that can evolve with your organization’s needs.

Remember that the Workspace ONE UEM API is not just about automating existing processes—it’s about reimagining what’s possible in device management. The combination of comprehensive API coverage, robust authentication, and powerful automation capabilities creates opportunities for innovation that were previously impossible.

The investment in mastering API integration pays dividends through improved operational efficiency, enhanced security posture, and the ability to address unique organizational requirements that would otherwise be impossible to meet. As you continue to develop your expertise in this area, you’ll find that the API becomes an indispensable tool for delivering world-class device management capabilities that truly serve your business objectives.

Leave a Comment

Your email address will not be published. Required fields are marked *