Skip to content

Commit

Permalink
Added Remove-TeamViewerDuplicateDevicesV2 example script
Browse files Browse the repository at this point in the history
The script searches for duplicate devices (where duplicate = have the same alias)
and removes the devices with an older last-seen timestamp.

Resolves JIRA task TEAM-60324.
  • Loading branch information
AchilleasMitos-TV committed Nov 22, 2024
1 parent 9b21a8e commit ec720fa
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 0 deletions.
44 changes: 44 additions & 0 deletions Remove-TeamViewerDuplicateDevicesV2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Remove-TeamViewerDuplicateDevicesV2

Removes TeamViewer devices (MDv2) that have a duplicate counterpart in the same company.

The script fetches a list of TeamViewer devices (MDv2) of the TeamViewer company that corresponds to a given API token.
The list will be searched for devices that have the same name (alias). Duplicate devices will be sorted by their last seen timestamp,
and the older ones will be removed.

## Prerequisites

This script requires the `TeamViewerPS` powershell module to be installed in at least Version 2.1.0.

```powershell
Install-Module TeamViewerPS
```

## Examples

### List removal candidate devices

```powershell
Remove-TeamViewerDuplicateDevicesV2 -WhatIf
```

### Remove old duplicate devices. User needs to confirm

```powershell
Remove-TeamViewerDuplicateDevicesV2
```

### Remove old duplicate devices without further confirmation

```powershell
Remove-TeamViewerDuplicateDevicesV2 -Force
```

## More help

To get further help about the script and its parameters, execute the
`Get-Help` PowerShell cmdlet:

```powershell
Get-Help -Detailed .\Remove-TeamViewerDuplicateDevicesV2.ps1
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2019-2024 TeamViewer Germany GmbH
# See file LICENSE

BeforeAll {
$testApiToken = [securestring]@{}

. "$PSScriptRoot\Remove-TeamViewerDuplicateDevicesV2.ps1" -ApiToken $testApiToken -InformationAction SilentlyContinue

Mock Get-TeamViewerCompanyManagedDevice { @(
[pscustomobject]@{ TeamViewerId = '123456789'; Name = 'unique device'; LastSeenAt = [datetime]'2024-12-16' },
[pscustomobject]@{ TeamViewerId = 'older device A'; Name = 'duplicate device A'; LastSeenAt = [datetime]'2024-12-17' },
[pscustomobject]@{ TeamViewerId = 'online device A'; Name = 'duplicate device A'; IsOnline = $True },
[pscustomobject]@{ TeamViewerId = 'newer device B'; Name = 'duplicate device B'; LastSeenAt = [datetime]'2024-12-18' },
[pscustomobject]@{ TeamViewerId = 'newer device A'; Name = 'duplicate device A'; LastSeenAt = [datetime]'2024-12-19' },
[pscustomobject]@{ TeamViewerId = 'older device B'; Name = 'duplicate device B'; LastSeenAt = [datetime]'2024-12-17' }
) }

Mock Remove-TeamViewerManagedDeviceManagement -RemoveParameterValidation 'Device' {}
}

Describe 'Remove-TeamViewerDuplicateDevicesV2' {

It 'Should not remove any devices if -WhatIf parameter has been set' {
$result = (Remove-TeamViewerDuplicateDevicesV2 -force:$false -WhatIf)
$result | Should -HaveCount 3
$result[0].TeamViewerId | Should -Be 'older device A'
$result[0].Status | Should -Be 'Unchanged'
$result[1].TeamViewerId | Should -Be 'newer device A'
$result[1].Status | Should -Be 'Unchanged'
$result[2].TeamViewerId | Should -Be 'older device B'
$result[2].Status | Should -Be 'Unchanged'

Assert-MockCalled Get-TeamViewerCompanyManagedDevice -Times 1 -Scope It
Assert-MockCalled Remove-TeamViewerManagedDeviceManagement -Times 0 -Scope It
}

It 'Should remove duplicate devices with an older last-seen timestamp' {
$result = (Remove-TeamViewerDuplicateDevicesV2 -force:$true)
$result | Should -HaveCount 3
$result[0].TeamViewerId | Should -Be 'older device A'
$result[0].Status | Should -Be 'Removed'
$result[1].TeamViewerId | Should -Be 'newer device A'
$result[1].Status | Should -Be 'Removed'
$result[2].TeamViewerId | Should -Be 'older device B'
$result[2].Status | Should -Be 'Removed'

Assert-MockCalled Get-TeamViewerCompanyManagedDevice -Times 1 -Scope It
Assert-MockCalled Remove-TeamViewerManagedDeviceManagement -Times 3 -Scope It
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<#
.SYNOPSIS
Removes TeamViewer duplicate devices (MDv2) based on their alias.
.DESCRIPTION
Removes TeamViewer devices (MDv2) that have a duplicate counterpart in the same company.
The script fetches a list of TeamViewer devices (MDv2) of the TeamViewer company that corresponds to a given API token.
The list will be searched for devices that have the same name (alias). Duplicate devices will be sorted by their last seen timestamp,
and the older ones will be removed.
.PARAMETER ApiToken
The TeamViewer API token to use.
Must be a user access token.
The token requires the following access permissions: company admin
.PARAMETER Force
If set, the script will NOT ask the user for confirmation of the removal.
The default value is `false`, causing the script to ask the user one more time before starting to remove devices.
.EXAMPLE
Remove-TeamViewerDuplicateDevicesV2'
.EXAMPLE
Remove-TeamViewerDuplicateDevicesV2 -WhatIf
.EXAMPLE
Remove-TeamViewerDuplicateDevicesV2 -Force
.NOTES
This script requires the TeamViewerPS module to be installed.
This can be done using the following command:
```
Install-Module TeamViewerPS
```
Copyright (c) 2019-2024 TeamViewer Germany GmbH
See file LICENSE
Version 2.1
#>

[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory = $true)]
[securestring] $ApiToken,

[switch] $Force = $false
)

if (-Not $MyInvocation.BoundParameters.ContainsKey('ErrorAction')) {
$script:ErrorActionPreference = 'Stop'
}
if (-Not $MyInvocation.BoundParameters.ContainsKey('InformationAction')) {
$script:InformationPreference = 'Continue'
}

function Install-TeamViewerModule {
$module = Get-Module TeamViewerPS

if (!$module) {
Install-Module TeamViewerPS
}
elseif ($module.Version -lt '2.1.0') {
Update-Module TeamViewerPS
}
}

function Remove-TeamViewerDuplicateDevicesV2 {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param([bool]$force, [bool]$is_verbose)

$devices = @(Get-TeamViewerCompanyManagedDevice -ApiToken $ApiToken)

$name_to_device_map = @{}
ForEach ($device in $devices) {
if ($null -eq $name_to_device_map[$device.Name]) {
$name_to_device_map[$device.Name] = New-Object System.Collections.Generic.List[System.Object]
}
$name_to_device_map[$device.Name].Add($device)
}

$name_to_device_map_sorted = @{}
$name_to_device_map.GetEnumerator() | ForEach-Object {
# Sort duplicate devices by LastSeenAt.
# Older devices should go first.
$offline_duplicate_devices = @(($_.Value | Where-Object { !$_.IsOnline -and $_.LastSeenAt }) | Sort-Object { $_.LastSeenAt })

if ($offline_duplicate_devices.Count -gt 0) {
if ($offline_duplicate_devices.Count -lt $_.Value.Count) {
# There were some online duplicate devices --> remove all of the offline
$name_to_device_map_sorted.Add($_.Key, $offline_duplicate_devices)
}
else {
# No online duplicate devices --> the last one is the "good" device --> skip it
$devices_to_remove = $offline_duplicate_devices | Select-Object -SkipLast 1
if ($null -ne $devices_to_remove) {
$name_to_device_map_sorted.Add($_.Key, $devices_to_remove)
}
}
}
}

if ($is_verbose) {
Write-Information 'All company devices:'
Write-Information ($devices | Format-List | Out-String)
}

if (!$name_to_device_map_sorted.Count) {
Write-Information 'No duplicate devices found. Exiting...'

exit
}

Write-Information 'Found the following devices that have a duplicate alias to other devices in your company, and have been offline for longer:'
Write-Information ($name_to_device_map_sorted | Format-List | Out-String)

if ($name_to_device_map_sorted.Count -gt 0 -And
!$WhatIfPreference -And
!$force -And
!$PSCmdlet.ShouldContinue('Do you really want to remove those devices?', 'Remove managed devices')) {
Write-Information 'Aborting...'

exit
}

$name_to_device_map_sorted.GetEnumerator() | ForEach-Object {
$duplicate_devices_to_be_deleted = $_.Value

ForEach ($device_to_be_deleted in $duplicate_devices_to_be_deleted) {
$status = 'Unchanged'

if ($force -Or $PSCmdlet.ShouldProcess($device_to_be_deleted.TeamViewerId, 'Remove device')) {
try {
Remove-TeamViewerManagedDeviceManagement -ApiToken $ApiToken -Device $device_to_be_deleted

$status = 'Removed'
}
catch {
Write-Warning "Failed to remove device '$($device_to_be_deleted.Name)' with TeamViewerID: '$($device_to_be_deleted.TeamViewerId)'"

$status = 'Failed'
}
}
Write-Output ([pscustomobject]@{
Name = $device_to_be_deleted.Name
ManagementId = $device_to_be_deleted.Id
LastSeen = $device_to_be_deleted.LastSeenAt
TeamViewerID = $device_to_be_deleted.TeamViewerId
Status = $status
})
}
}
}

if ($MyInvocation.InvocationName -ne '.') {
Install-TeamViewerModule

$is_verbose = $PSBoundParameters.ContainsKey('Verbose')

Remove-TeamViewerDuplicateDevicesV2 -force $Force -is_verbose $is_verbose
}

0 comments on commit ec720fa

Please sign in to comment.