Remove inactive computers

HeathMell
HeathMell Posts: 1

I'm using TeamViewer 13 Corporate, and I've been using it for several years now. 

Many of the computers in our organization have been retired or changed out, but the naming convention for TeamViewer computer is the same for all of our users regarless if they've had one, two, three or even a hundred computers assigned to them.  Is it possible to add a feature that would allow you to search for computers that have been offline for a cetain time frame, like two or three moths for example?  Then from that list, be able to do a bulk delete?  I'm aware that the bulk delete has been asked for in many other posts as well, and it makes no sense to me that this feature would not be available by design.

Also, there are many computers listed in "Unnamed devices" folder, for one time connects I've used in the past, is there no way I can delete these from my system as well?  I'm just trying to make my console clean and relevant, but you guys seem to be tying our hands with these basic functions by only permitting us to delete computers one at a time.

I would apreciate any response from TeamViewer staff on this matter.

Thank you

Best Answers

Answers

  • We would also very much like the feature to bulk remove retired computers. We recently reinstalled 4000 units with Windows 10, thus reinstalling teamviewer. Now, they alle are doubled in the console.

  • roryschmitz
    roryschmitz Posts: 6 ✭✭

    @KyleHoneycutt Brilliant!  Is this just a powershell cmdlet or do we run this from somewhere else?

  • @roryschmitz  Indeed it's in PowerShell.

    Hope you enjoy!

  • roryschmitz
    roryschmitz Posts: 6 ✭✭

    @KyleHoneycutt Thanks, I'm getting the following errors when it's running through the script.  It appears to find all of the stale comptures out there, but is this error pops up for each machines:

    Invoke-WebRequest : {"error":"invalid_token","error_description":"Access token does not have the required permissions
    for this function.","error_code":2}
    At C:\Downloads\TV_Cleanup.ps1:30 char:21
    + ... Invoke-WebRequest -Uri "Https://webapi.teamviewer.com/api ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
    eption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
    Deleted device: COMPUTERNAME

    Unsure why the access token wouldn't have the required permissions?  We only have the one token that I set up.  Maybe I'm not using the correct API token, but I just copied it from the Design & Deploy area in the web admin.  It's the one under custom TV Host package I created.  Is that the correct token?

  • roryschmitz
    roryschmitz Posts: 6 ✭✭

    @KyleHoneycutt That worked!  You're a life saver and I really appreciate it.

  • @roryschmitzMy pleasure, glad I could help!

  • roryschmitz
    roryschmitz Posts: 6 ✭✭

    @KyleHoneycutt I ended up posting your great solution over at the Spiceworks forums as well and linked back to here.  Hope that is OK?  If there's a way I can buy you a beer or two, please let me know. 

    Here's the discussion link:  [The URL link has been removed by a moderator.]

     

  • @roryschmitz Perfectly fine with me, spread it around as much as you wish! If you're ever in the Dallas area hit me up and we can definitely have a few beers. Cheers!

  • IanFriis
    IanFriis Posts: 3

    Hi there!

    Thanks a lot for providing a solution here.. :-)

    I am having another issue though, regarding the comparison of the dates. I'm trying to clean out devices not seen in a year, so i modified the script to use '360' instead of '30', but i don't think that is the issue - the problem is the time/date formats. I get a lot of this error (in danish):

    Could not compare "03/21/2018 00:00:00" to "søndag den 11. marts 2018". Error: "Cannot convert value "søndag den 11. marts 2018" to type "System.DateTime". Error: "Strengen blev ikke genkendt som en gyldig DateTime. Der er et ukendt ord, der starter ved indekset 0.""
    At C:\Users\adm_ianfje\Documents\TeamViewer_Cleanup_Inactive_Computers_Script.ps1:31 char:25
    + if ($DateLastSeen -le $360Days)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : ComparisonFailure

    I am totally new at powershell scripting, so i got no clue how to fix this. Please help. :-)

  • roryschmitz
    roryschmitz Posts: 6 ✭✭

    I'm not a PS expert either, but as a quick test, I modified this line:

    $30Days = ((Get-Date).AddDays(-30)).GetDateTimeFormats()[5]

    to this:

    $30Days = ((Get-Date).AddDays(-365)).GetDateTimeFormats()[5]

    and I did not receive any errors.  That could also be due to mine not finding any machines older than 365 days as I just removed everything older than 30 days.   It's a little messy, but it might be a quick test.  There shouldn't be any reason you couldn't replace '30' with '365' throughout the script.

  • @IanFriis Hey Ian,

    I'm not sure if PowerShell can convert the Danish language into a System.DateTime Object which is why you're getting that error.

    The way the script works is that it gets the date string from TeamViewer and then converts that string into a DateTime object so that it can be used as a comparison operator. Without converting it, it can't compare correctly.

    I think the easiest thing for you to do would be to change your local system language to English or run the Script on a server that has English as the OS language.

    If that workaround isn't feasible for you just let me know and we can try to come up with another solution for you.

  • IanFriis
    IanFriis Posts: 3

    @KyleHoneycutt @roryschmitz thank you for replying :)

    I solved the problem by setting my system language to english on the computer running the script. After a reboot the script ran without errors!

    Great to finally see those outdated computers removed from our list. 

    Thanks again - have a great day! =)

  • zensbert
    zensbert Posts: 1

    Hi Kyle, we are getting the same error as Ian. Our system runs in german language and we would like to keep it that way. The error we receive is:

    Could not compare "03/13/2019 00:00:00" to "19.9.2018". Error: "Cannot convert value "19.9.2018" to type "System.DateTime".

    i tried a little bit with converting the format but i didn´t manage to get this script running.

    Thanks in advance,

    Thomas

  • BambooHR
    BambooHR Posts: 1

    This was a great help. I decided to recreate this in Python 3. If anyone is interested, let me know!

  • trvadmin
    trvadmin Posts: 1

    Hi,

    Not sure if it still relevant but i had the same issue with the German format on my Win10 device, and after changing to the English (United states) formats the script was working correctly.

     

     

  • Hi @Closed account 

    How can I run this script? sorry i'm not good with technolgy

    For the home studio

  • Fule
    Fule Posts: 1

    Thanks for the script I modified it so it works and all languages (I think)
    Example of running the script Delete-staledevices.ps1 -token "Apikey" -staledays 150

    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$True)] [int32]$StaleDays,
    [Parameter(Mandatory=$True)] [String] $Token = ""
    )



    $bearer = "Bearer",$token
    $counter = 0

    $header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $header.Add("authorization", $bearer)

    $devices = (Invoke-RestMethod -Uri "Https://webapi.teamviewer.com/api/v1/devices" -Method Get -Headers $header).devices


    $Days = ((Get-Date).AddDays(-$staleDays))
    $Days = Get-Date $Days -Format s


    ForEach($device in $devices)
    {

    if ($device.online_state -eq "Offline")
    {

    $ID = $device.device_id

    $Lastseen = $device.last_seen

    if ($Lastseen -ne $null)
    {

    $LastSeen = ($device.last_seen).Split("T")[0]
    [datetime]$DateLastSeen = $LastSeen

    if ($DateLastSeen -le $Days)
    {

    $counter ++

    Invoke-WebRequest -Uri "Https://webapi.teamviewer.com/api/v1/devices/$ID" -Method Delete -Headers $header -UseBasicParsing
    Write-Host "Deleted device:"$device.alias $counter -ForegroundColor Yellow


    }$Lastseen = $null
    }
    }
    }

     

  • [CmdletBinding()]
    param(
    [Parameter(Mandatory=$True)] [int32]$StaleDays,
    [Parameter(Mandatory=$True)] [String] $Token = ""
    )



    $bearer = "Bearer",$token
    $counter = 0

    $header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $header.Add("authorization", $bearer)

    $devices = (Invoke-RestMethod -Uri "Https://webapi.teamviewer.com/api/v1/devices" -Method Get -Headers $header).devices


    $Days = ((Get-Date).AddDays(-$staleDays))
    $Days = Get-Date $Days -Format s


    ForEach($device in $devices)
    {

    if ($device.online_state -eq "Offline")
    {

    $ID = $device.device_id

    $Lastseen = $device.last_seen

    if ($Lastseen -ne $null)
    {

    $LastSeen = ($device.last_seen).Split("T")[0]
    [datetime]$DateLastSeen = $LastSeen

    if ($DateLastSeen -le $Days)
    {

    $counter ++

    Invoke-WebRequest -Uri "Https://webapi.teamviewer.com/api/v1/devices/$ID" -Method Delete -Headers $header -UseBasicParsing
    Write-Host "Deleted device:"$device.alias $counter -ForegroundColor Yellow


    }$Lastseen = $null
    }
    }
    }

     

     

    Nice the this script work perfectly in German !!!

     

     

    thx !!!

  • Thanks for the script. How can I apply this on our teamviewer environment? 

    Many thanks!

  • Save the Above Script in Name Like "TeamViewerDeleteMachines_olderX_Days.ps1"

    simply start CMD run this command with AdminRights

    powershell "PATHtoYOURpowershellScript\TeamViewerDeleteMachines_olderX_Days.ps1" -token YOURTOKENfromTeamVIEWER -staledays 60

    The -staledays 60 stands for the Days you want to delete

     

  • Perfect, this is working! Can I schedule this script on my teamviewer environment?

  • roryschmitz
    roryschmitz Posts: 6 ✭✭

    Yes, you can schedule this.  I run a scheduled task every day to clear out the stale machines.  It keeps things tidy and manageable.

  • PaulM_uk
    PaulM_uk Posts: 2 ✭✭

    I've just tried to run this and I'm finding that the returned object does not contain the last_seen attribute

    Anybody else seen this or have any ideas?

  • PaulM_uk
    PaulM_uk Posts: 2 ✭✭

    Sorted my own issue. If a device is currently online, the last_seen attribute is not returned

    Seems that if you export an array of objects to a csv, it determines the field headers from the first array object.

  • jelrik
    jelrik Posts: 2 ✭✭

    question is it possible to filter out with groups?


    other question we are using vdi envirmont and on all the clients there is a teamvieuwer application running. But when the user end of the day close the vdi new vdi with same id will be created automaticly . And in teamvieuwer then I see 2 the same id only 1 is offline 1 is online. Any solution to this?

  • ParisCM
    ParisCM Posts: 3 ✭✭
    edited 1:07PM

    Our machines do no appear in the "Groups" section on the site. They are under "Device Groups" instead. The means it uses https://webapi.teamviewer.com/api/v1/managed/devices

    I've been bouncing around chatgpt and gemini to create a script that removes devices not seen in the last 90 days so excuse the syntax if it's a little long winded but it does the trick. I'm going to leave this on a sever running via task scheduler so I wanted it to log which machines are being deleted.

    $token = "TOKENID" # Replace with your actual token
    
    $bearer = "Bearer " + $token
    
    $inactiveThreshold = (New-TimeSpan -Seconds (86400 * 90)).TotalMilliseconds # 90 days in milliseconds
    
    $currentTime = Get-Date
    
    $deletedDevices = @() # Array to store deleted device information
    
    $csvPath = "C:\logs\tvdeleteddevices.csv"
    
    
    
    
    $header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    
    $header.Add("authorization", $bearer)
    
    
    
    
    # Function to delete a device
    
    function Remove-TeamViewerDevice {
    
     param (
    
      [Parameter(Mandatory=$true)]
    
      [string] $deviceId,
    
      [string] $deviceName,
    
      [string] $lastSeen
    
     )
    
    
    
    
      $managedDevicesUri = "https://webapi.teamviewer.com/api/v1/managed/devices/$deviceId"
    
    
    
    
     try {
    
      Invoke-RestMethod -Uri $managedDevicesUri -Method Delete -Headers $header
    
      Write-Host "Device $deviceId deleted successfully."
    
      $deviceObject = New-Object PSObject -Property @{
    
       "Device ID" = $deviceId
    
       "Device Name" = $deviceName
    
       "Last Seen" = $lastSeen
    
      }
    
      return $deviceObject
    
     } catch {
    
      Write-Host "Error deleting device ${deviceId}: " -NoNewline
    
      Write-Host $_.Exception.Message
    
      return $null
    
     }
    
    }
    
    
    
    
    # Initial call for devices
    
    try {
    
      $managedDevicesUri = "https://webapi.teamviewer.com/api/v1/managed/devices"
    
     $response = Invoke-RestMethod -Uri $managedDevicesUri -Method Get -Headers $header
    
     if ($response.resources -ne $null) {
    
      foreach ($device in $response.resources) {
    
       $deviceName = $device.name
    
       $lastSeen = if ($device.last_seen -ne $null) {[DateTime]::Parse($device.last_seen).ToString("yyyy-MM-dd HH:mm:ss")} else {"Never"}
    
        
    
       # Skip online devices and devices with missing Last Seen
    
       if ($device.isOnline -eq $true -or $device.last_seen -eq $null) {
    
         continue
    
       }
    
        
    
       # Check if the device is inactive
    
       if (($currentTime - [DateTime]::Parse($device.last_seen)).TotalMilliseconds -gt $inactiveThreshold) {
    
        $deletedDevice = Remove-TeamViewerDevice -deviceId $device.id -deviceName $deviceName -lastSeen $lastSeen
    
        if ($deletedDevice -ne $null) {
    
         $deletedDevices += $deletedDevice
    
        }
    
       }
    
      }
    
       
    
      # Handle pagination if a nextPaginationToken is provided (optional)
    
      # ... (similar logic as before)
    
     } else {
    
      Write-Host "No devices found or empty response."
    
     }
    
    } catch {
    
     Write-Host "Error retrieving devices: $($_.Exception.Message)"
    
    }
    
    
    
    
    # Export deletedDevices array to CSV (if any)
    
    if ($deletedDevices.Count -gt 0) {
    
     $deletedDevices | Select-Object 'Device ID', 'Device Name', 'Last Seen' | Export-Csv -Path $csvPath -NoTypeInformation -Append
    
     Write-Host "Deleted devices list exported to $csvPath"
    
    } else {
    
     Write-Host "No devices were deleted based on the current settings."
    
    }