<# .SYNOPSIS Workspace ONE Update OnDemand App, let you select current installed devices for a specific version of an application and push a newer version to a selection of these devices. .DESCRIPTION This script tries to cover the missing capability of Workspace ONE UEM to push updates to devices which have manually installed an older version but need to be updated. Especially for security relevant updates (eg. Adobe Reader, Firefox, etc.) this was an important but missing function in one of my recent projects. 1. Update the [Declarations] Section to match your environment 2. If you run this script you will be prompted 4 times 2.1 Select the current installed version to identify targeted devices 2.2 Select the target version of the application, it will be filtered to the same BundleId 2.3 Select one or more devices (eg. for testing before send the command to all devices) 2.4 Final confirmation if you really want to initate the PUSH INSTALL to your selected devices .PARAMETER No parameters yet .INPUTS None .OUTPUTS Console output only, maybe create a log file in an upcoming version .NOTES Version: 1.2 Author: Alexander Askin Creation Date: 21. August 2020 Purpose/Change: v1.0 2020-08-06 - Initial Development v1.1 2020-08-21 - Fixed a typo in Install-ApplicationByDeviceID and Remove-ApplicationByDeviceID v1.2 2020-08-23 - Switched on Get-DeviceIDsByInstalledApplicationID to the "GET /apps/{uuid}/devices" endpoint as "GET /apps/internal/{applicationid}/devices" did not work consistently as expected. Added user-cancel handling. .EXAMPLE WorkspaceONE_Update_OnDemand_App.ps1 #> Write-Host " _ __ __ ____ _ ______ __ ________ ___ " Write-Host " | | /| / /__ ____/ /__ ___ ___ ___ ________ / __ \/ |/ / __/ / / / / __/ |/ / " Write-Host " | |/ |/ / _ \/ __/ '_/(_-/API/help/#!/Apps/Apps_Search for more details on filters Function Get-Applications { Write-Host("Getting all Applications") -NoNewline -ForegroundColor Cyan $endpointURL = $URL + "/mam/apps/search?status=Active&platform=WinRT" $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header Write-Host(" - found: " + $webReturn.Total) return $webReturn } # Return all devices which have a specific application version installed Function Get-DeviceIDsByInstalledApplicationID($WorkSpaceONEApplicationUUID, $WorkSpaceONEApplicationName) { Write-Host("Getting all Devices having " + $WorkSpaceONEApplicationName + " installed") -NoNewline -ForegroundColor Cyan $endpointURL = $URL + "/mam/apps/" + $WorkSpaceONEApplicationUUID + "/devices?isinstalled=true" $webReturn = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $header Write-Host(" - found: " + $webReturn.TotalResults) return $webReturn } # Invoke uninstallation of an application on a spefic device; not used in this script - kept it as reference Function Remove-ApplicationByDeviceID ($WorkSpaceONEApplicationID, $WorkSpaceONEDeviceID){ $endpointURL = $URL + "/mam/apps/internal/" + $WorkSpaceONEApplicationID + "/uninstall" $body = @() $body = [pscustomobject]@{ 'DeviceID' = $WorkSpaceONEDeviceID; } $json = $body | ConvertTo-Json $webReturn = Invoke-RestMethod -Method Post -Uri $endpointURL -Headers $header -Body $json return $webReturn } # Invoke installation of an application on a spefic device Function Install-ApplicationByDeviceID ($WorkSpaceONEApplicationID, $WorkSpaceONEDeviceID){ $endpointURL = $URL + "/mam/apps/internal/" + $WorkSpaceONEApplicationID + "/install" $body = @() $body = [pscustomobject]@{ 'DeviceId' = $WorkSpaceONEDeviceID; } $json = $body | ConvertTo-Json $webReturn = Invoke-RestMethod -Method Post -Uri $endpointURL -Headers $header -Body $json return $webReturn } #-----------------------------------------------------------[Execution]------------------------------------------------------------ Write-Host "" if(Check-ConsoleVersion){ # Get all available Win32 Apps which are Active $AllApplications = (Get-Applications).Application # Select Source and Target Version $SelectedSourceApplication = $AllApplications | Select-Object -Property ApplicationName,AppVersion,AssignmentStatus,AssignedDeviceCount,InstalledDeviceCount,NotInstalledDeviceCount,SmartGroups,BundleId,Id,Uuid | Out-GridView -Title "Select the Current Application which needs to be updated; InstalledDeviceCount is not always accurate" -OutputMode Single if(!$SelectedSourceApplication){Write-Host "No source application selected. Aborted." -ForegroundColor Red; exit} $SelectedTargetApplication = $AllApplications | Select-Object -Property ApplicationName,AppVersion,AssignmentStatus,AssignedDeviceCount,InstalledDeviceCount,NotInstalledDeviceCount,SmartGroups,BundleId,Id,Uuid | Where-Object BundleId -eq $SelectedSourceApplication.BundleId |Sort-Object -Property AppVersion -Descending | Out-GridView -Title "Select the Target Application you like to update to" -OutputMode Single if(!$SelectedTargetApplication){Write-Host "No target application selected. Aborted." -ForegroundColor Red; exit} # Get all devices having the old version installed $DeviceIDsApplicationCurrentlyInstalled = (Get-DeviceIDsByInstalledApplicationID $SelectedSourceApplication.Uuid $SelectedSourceApplication.ApplicationName).devices # Get all devices to have DeviceFriendlyName,SerialNumber,Model,LastSeen available $AllDevices = (Get-Devices).Devices # Filter the current list $DeviceList = @() foreach ($Device in $AllDevices){ if($DeviceIDsApplicationCurrentlyInstalled.device_id.Contains($Device.Id.Value)){ $DeviceList += $Device } } # Select one or more devices $SelectedDevices = $DeviceList | Select-Object -Property DeviceFriendlyName,SerialNumber,Model,LastSeen,Id | Out-GridView -Title "Select one or more devices you like to push install $($SelectedTargetApplication.ApplicationName) $($SelectedTargetApplication.AppVersion), use CTR+A to select them all" -PassThru | Select-Object -ExpandProperty Id if($SelectedDevices.Count){ $NumberOfSelectedDevices = $SelectedDevices.Count }elseif($SelectedDevices){ $NumberOfSelectedDevices = 1 }else{ Write-Host "No device(s) selected. Aborted." -ForegroundColor Red; exit } # Ask for confirmation $wscriptShell = New-Object -ComObject Wscript.Shell $Confirmation = $wscriptShell.PopUp("Are you sure you want to update $($SelectedDevices.Count) device(s)`nfrom $($SelectedSourceApplication.ApplicationName) $($SelectedSourceApplication.AppVersion) to $($SelectedTargetApplication.ApplicationName) $($SelectedTargetApplication.AppVersion)", 0,"Final Confirmation",4 + 32) # And Action if($Confirmation -eq "6"){ foreach ($Device in $SelectedDevices){ Write-Host "Initiate Install-ApplicationByDeviceID $($SelectedTargetApplication.Id.Value) $($Device.Value)" -ForegroundColor Yellow Install-ApplicationByDeviceID $SelectedTargetApplication.Id.Value $Device.Value } Write-Host "All done." -ForegroundColor Green }else{ Write-Host "Aborted." -ForegroundColor Red } }else{ Write-Host "Unable to connect to Workspace ONE UEM Console" -ForegroundColor Red }