Skip to content

Commit b07790a

Browse files
Merge pull request #30 from teamviewer/TEAM-60324_add_cleanup_duplicate_devices_script
Added Remove-TeamViewerDuplicateDevicesV2 example script
2 parents 9b21a8e + ec720fa commit b07790a

File tree

3 files changed

+255
-0
lines changed

3 files changed

+255
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Remove-TeamViewerDuplicateDevicesV2
2+
3+
Removes TeamViewer devices (MDv2) that have a duplicate counterpart in the same company.
4+
5+
The script fetches a list of TeamViewer devices (MDv2) of the TeamViewer company that corresponds to a given API token.
6+
The list will be searched for devices that have the same name (alias). Duplicate devices will be sorted by their last seen timestamp,
7+
and the older ones will be removed.
8+
9+
## Prerequisites
10+
11+
This script requires the `TeamViewerPS` powershell module to be installed in at least Version 2.1.0.
12+
13+
```powershell
14+
Install-Module TeamViewerPS
15+
```
16+
17+
## Examples
18+
19+
### List removal candidate devices
20+
21+
```powershell
22+
Remove-TeamViewerDuplicateDevicesV2 -WhatIf
23+
```
24+
25+
### Remove old duplicate devices. User needs to confirm
26+
27+
```powershell
28+
Remove-TeamViewerDuplicateDevicesV2
29+
```
30+
31+
### Remove old duplicate devices without further confirmation
32+
33+
```powershell
34+
Remove-TeamViewerDuplicateDevicesV2 -Force
35+
```
36+
37+
## More help
38+
39+
To get further help about the script and its parameters, execute the
40+
`Get-Help` PowerShell cmdlet:
41+
42+
```powershell
43+
Get-Help -Detailed .\Remove-TeamViewerDuplicateDevicesV2.ps1
44+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright (c) 2019-2024 TeamViewer Germany GmbH
2+
# See file LICENSE
3+
4+
BeforeAll {
5+
$testApiToken = [securestring]@{}
6+
7+
. "$PSScriptRoot\Remove-TeamViewerDuplicateDevicesV2.ps1" -ApiToken $testApiToken -InformationAction SilentlyContinue
8+
9+
Mock Get-TeamViewerCompanyManagedDevice { @(
10+
[pscustomobject]@{ TeamViewerId = '123456789'; Name = 'unique device'; LastSeenAt = [datetime]'2024-12-16' },
11+
[pscustomobject]@{ TeamViewerId = 'older device A'; Name = 'duplicate device A'; LastSeenAt = [datetime]'2024-12-17' },
12+
[pscustomobject]@{ TeamViewerId = 'online device A'; Name = 'duplicate device A'; IsOnline = $True },
13+
[pscustomobject]@{ TeamViewerId = 'newer device B'; Name = 'duplicate device B'; LastSeenAt = [datetime]'2024-12-18' },
14+
[pscustomobject]@{ TeamViewerId = 'newer device A'; Name = 'duplicate device A'; LastSeenAt = [datetime]'2024-12-19' },
15+
[pscustomobject]@{ TeamViewerId = 'older device B'; Name = 'duplicate device B'; LastSeenAt = [datetime]'2024-12-17' }
16+
) }
17+
18+
Mock Remove-TeamViewerManagedDeviceManagement -RemoveParameterValidation 'Device' {}
19+
}
20+
21+
Describe 'Remove-TeamViewerDuplicateDevicesV2' {
22+
23+
It 'Should not remove any devices if -WhatIf parameter has been set' {
24+
$result = (Remove-TeamViewerDuplicateDevicesV2 -force:$false -WhatIf)
25+
$result | Should -HaveCount 3
26+
$result[0].TeamViewerId | Should -Be 'older device A'
27+
$result[0].Status | Should -Be 'Unchanged'
28+
$result[1].TeamViewerId | Should -Be 'newer device A'
29+
$result[1].Status | Should -Be 'Unchanged'
30+
$result[2].TeamViewerId | Should -Be 'older device B'
31+
$result[2].Status | Should -Be 'Unchanged'
32+
33+
Assert-MockCalled Get-TeamViewerCompanyManagedDevice -Times 1 -Scope It
34+
Assert-MockCalled Remove-TeamViewerManagedDeviceManagement -Times 0 -Scope It
35+
}
36+
37+
It 'Should remove duplicate devices with an older last-seen timestamp' {
38+
$result = (Remove-TeamViewerDuplicateDevicesV2 -force:$true)
39+
$result | Should -HaveCount 3
40+
$result[0].TeamViewerId | Should -Be 'older device A'
41+
$result[0].Status | Should -Be 'Removed'
42+
$result[1].TeamViewerId | Should -Be 'newer device A'
43+
$result[1].Status | Should -Be 'Removed'
44+
$result[2].TeamViewerId | Should -Be 'older device B'
45+
$result[2].Status | Should -Be 'Removed'
46+
47+
Assert-MockCalled Get-TeamViewerCompanyManagedDevice -Times 1 -Scope It
48+
Assert-MockCalled Remove-TeamViewerManagedDeviceManagement -Times 3 -Scope It
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<#
2+
.SYNOPSIS
3+
Removes TeamViewer duplicate devices (MDv2) based on their alias.
4+
5+
.DESCRIPTION
6+
Removes TeamViewer devices (MDv2) that have a duplicate counterpart in the same company.
7+
The script fetches a list of TeamViewer devices (MDv2) of the TeamViewer company that corresponds to a given API token.
8+
The list will be searched for devices that have the same name (alias). Duplicate devices will be sorted by their last seen timestamp,
9+
and the older ones will be removed.
10+
11+
.PARAMETER ApiToken
12+
The TeamViewer API token to use.
13+
Must be a user access token.
14+
The token requires the following access permissions: company admin
15+
16+
.PARAMETER Force
17+
If set, the script will NOT ask the user for confirmation of the removal.
18+
The default value is `false`, causing the script to ask the user one more time before starting to remove devices.
19+
20+
.EXAMPLE
21+
Remove-TeamViewerDuplicateDevicesV2'
22+
23+
.EXAMPLE
24+
Remove-TeamViewerDuplicateDevicesV2 -WhatIf
25+
26+
.EXAMPLE
27+
Remove-TeamViewerDuplicateDevicesV2 -Force
28+
29+
.NOTES
30+
This script requires the TeamViewerPS module to be installed.
31+
This can be done using the following command:
32+
33+
```
34+
Install-Module TeamViewerPS
35+
```
36+
37+
Copyright (c) 2019-2024 TeamViewer Germany GmbH
38+
See file LICENSE
39+
Version 2.1
40+
#>
41+
42+
[CmdletBinding(SupportsShouldProcess = $true)]
43+
param(
44+
[Parameter(Mandatory = $true)]
45+
[securestring] $ApiToken,
46+
47+
[switch] $Force = $false
48+
)
49+
50+
if (-Not $MyInvocation.BoundParameters.ContainsKey('ErrorAction')) {
51+
$script:ErrorActionPreference = 'Stop'
52+
}
53+
if (-Not $MyInvocation.BoundParameters.ContainsKey('InformationAction')) {
54+
$script:InformationPreference = 'Continue'
55+
}
56+
57+
function Install-TeamViewerModule {
58+
$module = Get-Module TeamViewerPS
59+
60+
if (!$module) {
61+
Install-Module TeamViewerPS
62+
}
63+
elseif ($module.Version -lt '2.1.0') {
64+
Update-Module TeamViewerPS
65+
}
66+
}
67+
68+
function Remove-TeamViewerDuplicateDevicesV2 {
69+
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
70+
param([bool]$force, [bool]$is_verbose)
71+
72+
$devices = @(Get-TeamViewerCompanyManagedDevice -ApiToken $ApiToken)
73+
74+
$name_to_device_map = @{}
75+
ForEach ($device in $devices) {
76+
if ($null -eq $name_to_device_map[$device.Name]) {
77+
$name_to_device_map[$device.Name] = New-Object System.Collections.Generic.List[System.Object]
78+
}
79+
$name_to_device_map[$device.Name].Add($device)
80+
}
81+
82+
$name_to_device_map_sorted = @{}
83+
$name_to_device_map.GetEnumerator() | ForEach-Object {
84+
# Sort duplicate devices by LastSeenAt.
85+
# Older devices should go first.
86+
$offline_duplicate_devices = @(($_.Value | Where-Object { !$_.IsOnline -and $_.LastSeenAt }) | Sort-Object { $_.LastSeenAt })
87+
88+
if ($offline_duplicate_devices.Count -gt 0) {
89+
if ($offline_duplicate_devices.Count -lt $_.Value.Count) {
90+
# There were some online duplicate devices --> remove all of the offline
91+
$name_to_device_map_sorted.Add($_.Key, $offline_duplicate_devices)
92+
}
93+
else {
94+
# No online duplicate devices --> the last one is the "good" device --> skip it
95+
$devices_to_remove = $offline_duplicate_devices | Select-Object -SkipLast 1
96+
if ($null -ne $devices_to_remove) {
97+
$name_to_device_map_sorted.Add($_.Key, $devices_to_remove)
98+
}
99+
}
100+
}
101+
}
102+
103+
if ($is_verbose) {
104+
Write-Information 'All company devices:'
105+
Write-Information ($devices | Format-List | Out-String)
106+
}
107+
108+
if (!$name_to_device_map_sorted.Count) {
109+
Write-Information 'No duplicate devices found. Exiting...'
110+
111+
exit
112+
}
113+
114+
Write-Information 'Found the following devices that have a duplicate alias to other devices in your company, and have been offline for longer:'
115+
Write-Information ($name_to_device_map_sorted | Format-List | Out-String)
116+
117+
if ($name_to_device_map_sorted.Count -gt 0 -And
118+
!$WhatIfPreference -And
119+
!$force -And
120+
!$PSCmdlet.ShouldContinue('Do you really want to remove those devices?', 'Remove managed devices')) {
121+
Write-Information 'Aborting...'
122+
123+
exit
124+
}
125+
126+
$name_to_device_map_sorted.GetEnumerator() | ForEach-Object {
127+
$duplicate_devices_to_be_deleted = $_.Value
128+
129+
ForEach ($device_to_be_deleted in $duplicate_devices_to_be_deleted) {
130+
$status = 'Unchanged'
131+
132+
if ($force -Or $PSCmdlet.ShouldProcess($device_to_be_deleted.TeamViewerId, 'Remove device')) {
133+
try {
134+
Remove-TeamViewerManagedDeviceManagement -ApiToken $ApiToken -Device $device_to_be_deleted
135+
136+
$status = 'Removed'
137+
}
138+
catch {
139+
Write-Warning "Failed to remove device '$($device_to_be_deleted.Name)' with TeamViewerID: '$($device_to_be_deleted.TeamViewerId)'"
140+
141+
$status = 'Failed'
142+
}
143+
}
144+
Write-Output ([pscustomobject]@{
145+
Name = $device_to_be_deleted.Name
146+
ManagementId = $device_to_be_deleted.Id
147+
LastSeen = $device_to_be_deleted.LastSeenAt
148+
TeamViewerID = $device_to_be_deleted.TeamViewerId
149+
Status = $status
150+
})
151+
}
152+
}
153+
}
154+
155+
if ($MyInvocation.InvocationName -ne '.') {
156+
Install-TeamViewerModule
157+
158+
$is_verbose = $PSBoundParameters.ContainsKey('Verbose')
159+
160+
Remove-TeamViewerDuplicateDevicesV2 -force $Force -is_verbose $is_verbose
161+
}

0 commit comments

Comments
 (0)