diff --git a/CHANGELOG.md b/CHANGELOG.md index 1917da7..9594c0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log + +## 2.5.0 (2026-02-11) + +### Updated + +- Updates `Get-TeamViewerRole` to list all the possible permissions +- Updates `Set-TeamViewerManagedDevice` with additional description endpoint + +### Added + +- Adds `Get-TeamViewerInstallationType` that returns the TV installation type (MSI, exe or Unknown) from locally installed TV client +- Adds `Get-TeamViewerRoleByUser` that returns the assigned role ids of the user +- Adds `Get-TeamViewerEffectivePermission` that lists all effective permissions in a TeamViewer company +- Adds new endpoint ID for function `Get-TeamViewerSsoDomain` + + ## 2.4.0 (2025-06-19) ### Added diff --git a/Cmdlets/Public/Get-TeamViewerInstallationType.ps1 b/Cmdlets/Public/Get-TeamViewerInstallationType.ps1 new file mode 100644 index 0000000..c87478d --- /dev/null +++ b/Cmdlets/Public/Get-TeamViewerInstallationType.ps1 @@ -0,0 +1,82 @@ +function Get-TeamViewerInstallationType { + + [CmdletBinding()] + [OutputType([string])] + param() + + $regPath = 'HKLM:\SOFTWARE\TeamViewer' + $regPathWow = 'HKLM:\SOFTWARE\WOW6432Node\TeamViewer' + $MsiDatabaseFound = $false + $MsiRegistryValueFound = $false + + + + #first check MSI database + try { + $MsiProduct = Get-CimInstance -ClassName Win32_Product -Filter "Name='TeamViewer'" -ErrorAction Stop + if ($MsiProduct) { + $MsiDatabaseFound = $true + } + } + catch { + Write-Verbose "MSI database check failed or TeamViewer not found via WMI" + } + + #check MsiInstallation value + foreach ($path in @($regPath, $regPathWow)) { + try { + $MsiInstallationValue = Get-ItemProperty -Path $path -Name 'MsiInstallation' -ErrorAction Stop + + if ($null -ne $MsiInstallationValue.MsiInstallation -and $MsiInstallationValue.MsiInstallation -eq 1) { + $MsiRegistryValueFound = $true + break + } + } + catch [System.Management.Automation.ItemNotFoundException] { + Write-Verbose "TeamViewer registry path not found: $path" + } + catch { + Write-Verbose "registry check for MsiInstallation failed at $path" + } + } + + if ($MsiDatabaseFound -and $MsiRegistryValueFound) { + return 'MSI' + } + + #check for exeInstallation + try { + $uninstallPaths = @( + 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' + ) + + foreach ($uninstallPath in $uninstallPaths) { + if (Test-Path -Path $uninstallPath) { + $teamViewerKey = Get-ChildItem -Path $uninstallPath -ErrorAction SilentlyContinue | + Where-Object { $_.GetValue('DisplayName') -like '*TeamViewer*' } + + if ($teamViewerKey) { + $uninstallValue = Get-ItemProperty -Path $teamViewerKey.PSPath -Name 'UninstallString' -ErrorAction SilentlyContinue + if ($uninstallValue -and $uninstallValue.UninstallString) { + #extract the executable path using regex + $uninstallFile = $uninstallValue.UninstallString -replace '^"?([^"]+)"?.*$', '$1' + $uninstallFile = $uninstallFile.Trim('"') + #verify if it's a file and if it exists + if (Test-Path -Path $uninstallFile -PathType Leaf) { + return 'exe' + } + else { + return 'Unknown' + } + } + } + } + } + } + catch { + Write-Verbose "Error checking UninstallString" + } + + return 'Unknown' +} diff --git a/Cmdlets/TeamViewerPS.psd1 b/Cmdlets/TeamViewerPS.psd1 index e9c9bd2..346bb3d 100644 --- a/Cmdlets/TeamViewerPS.psd1 +++ b/Cmdlets/TeamViewerPS.psd1 @@ -3,7 +3,7 @@ RootModule = 'TeamViewerPS.psm1' # Version number of this module. - ModuleVersion = '2.4.0' + ModuleVersion = '2.5.0' # Supported PSEditions. # CompatiblePSEditions = @() diff --git a/Docs/Help/Get-TeamViewerInstallationType.md b/Docs/Help/Get-TeamViewerInstallationType.md new file mode 100644 index 0000000..bd125f8 --- /dev/null +++ b/Docs/Help/Get-TeamViewerInstallationType.md @@ -0,0 +1,53 @@ +--- +external help file: TeamViewerPS-help.xml +Module Name: TeamViewerPS +online version: https://github.com/teamviewer/TeamViewerPS/blob/main/Docs/Help/Get-TeamViewerInstallationType.md +schema: 2.0.0 +--- + +# Get-TeamViewerInstallationType + +## SYNOPSIS + +Returns the TeamViewer installation type. + +## SYNTAX + +```powershell +Get-TeamViewerInstallationType +``` + +## DESCRIPTION + +The command checks the TeamViewer installation and returns the installation type (MSI or exe) and $null when it could not be determined. + +## EXAMPLES + +### Example 1 + +```powershell +PS /> Get-TeamViewerInstallationType +``` + +Returns the installation type of TeamViewer (MSI, exe or Unknown). + +## PARAMETERS + +### CommonParameters + +### None + +## INPUTS + +### None + +## OUTPUTS + +### System.String + +Returns a string indicating the installation type: "MSI", "exe", or "Unknown". + +## NOTES + +## RELATED LINKS + diff --git a/Docs/TeamViewerPS.md b/Docs/TeamViewerPS.md index b8581d3..9e37382 100644 --- a/Docs/TeamViewerPS.md +++ b/Docs/TeamViewerPS.md @@ -222,6 +222,8 @@ The following functions are available in this category: [`Get-TeamViewerId`](Help/Get-TeamViewerId.md) +[`Get-TeamViewerInstallationType`](Help/Get-TeamViewerInstallationType.md) + [`Get-TeamViewerService`](Help/Get-TeamViewerService.md) [`Get-TeamViewerVersion`](Help/Get-TeamViewerVersion.md) diff --git a/Tests/Public/Get-TeamViewerInstallationType.Tests.ps1 b/Tests/Public/Get-TeamViewerInstallationType.Tests.ps1 new file mode 100644 index 0000000..8edef48 --- /dev/null +++ b/Tests/Public/Get-TeamViewerInstallationType.Tests.ps1 @@ -0,0 +1,220 @@ +BeforeAll { + . "$PSScriptRoot\..\..\Cmdlets\Public\Get-TeamViewerInstallationType.ps1" + @(Get-ChildItem -Path "$PSScriptRoot\..\..\Cmdlets\Private\*.ps1") | ` + ForEach-Object { . $_.FullName } +} + +Describe 'Get-TeamViewerInstallationType' { + Context 'MsiInstallation - both database and registry present' { + BeforeAll { + Mock Get-CimInstance { + [PSCustomObject]@{ Name = 'TeamViewer' } + } + Mock Get-ItemProperty { + [PSCustomObject]@{ MsiInstallation = 1 } + } + } + + It 'Should return MSI when MSI database and MsiInstallation registry value are both present' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'MSI' + Assert-MockCalled Get-CimInstance -Times 1 + } + } + + Context 'MSI database found but no registry value' { + BeforeAll { + Mock Get-CimInstance { + [PSCustomObject]@{ Name = 'TeamViewer' } + } + Mock Get-ItemProperty { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + } + + It 'Should not return MSI if only database entry exists' { + $result = Get-TeamViewerInstallationType + $result | Should -Not -Be 'MSI' + } + } + + Context 'Registry value found but no MSI database entry' { + BeforeAll { + Mock Get-CimInstance { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ItemProperty { + [PSCustomObject]@{ MsiInstallation = 1 } + } + } + + It 'Should not return MSI if only registry value exists' { + $result = Get-TeamViewerInstallationType + $result | Should -Not -Be 'MSI' + } + } + + Context 'exeInstallation with valid UninstallString' { + BeforeAll { + Mock Get-CimInstance { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ItemProperty { + throw [System.Management.Automation.ItemNotFoundException]::new() + } -ParameterFilter { $Name -eq 'MsiInstallation' } + Mock Test-Path { $true } + Mock Get-ChildItem { + $registryKey = New-Object PSObject + $registryKey | Add-Member -MemberType NoteProperty -Name 'Name' -Value 'TeamViewer' + $registryKey | Add-Member -MemberType NoteProperty -Name 'PSPath' -Value 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\TeamViewer' + $registryKey | Add-Member -MemberType ScriptMethod -Name 'GetValue' -Value { return 'TeamViewer' } + $registryKey + } + Mock Get-ItemProperty { + [PSCustomObject]@{ + UninstallString = '"C:\Program Files\TeamViewer\uninstall.exe" /S' + } + } -ParameterFilter { $Name -eq 'UninstallString' } + } + + It 'Should return exe when UninstallString file exists' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'exe' + } + } + + Context 'Broken exeInstallation with missing UninstallString file' { + BeforeAll { + Mock Get-CimInstance { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ItemProperty { + throw [System.Management.Automation.ItemNotFoundException]::new() + } -ParameterFilter { $Name -eq 'MsiInstallation' } + Mock Get-ChildItem { + $registryKey = New-Object PSObject + $registryKey | Add-Member -MemberType NoteProperty -Name 'Name' -Value 'TeamViewer' + $registryKey | Add-Member -MemberType NoteProperty -Name 'PSPath' -Value 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\TeamViewer' + $registryKey | Add-Member -MemberType ScriptMethod -Name 'GetValue' -Value { return 'TeamViewer' } + $registryKey + } + Mock Get-ItemProperty { + [PSCustomObject]@{ + UninstallString = '"C:\Program Files\TeamViewer\uninstall.exe" /S' + } + } -ParameterFilter { $Name -eq 'UninstallString' } + + Mock Test-Path { + param($Path) + if ($Path -like '*:\Software\*' -or $Path -like '*:\HKEY_*') { + return $true + } + #file does not exist + return $false + } + } + + It 'Should return Unknown when UninstallString file does not exist' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'Unknown' + } + } + + Context 'TeamViewer not installed' { + BeforeAll { + Mock Get-CimInstance { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ItemProperty { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ChildItem { } + Mock Test-Path { $false } + } + + It 'Should return Unknown when TeamViewer is not installed' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'Unknown' + } + } + + Context 'Check both 32-bit and 64-bit registry paths for MSI' { + BeforeAll { + Mock Get-CimInstance { + [PSCustomObject]@{ Name = 'TeamViewer' } + } + Mock Get-ItemProperty { + param($Path) + if ($Path -like '*WOW6432Node*') { + [PSCustomObject]@{ MsiInstallation = 1 } + } + else { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + } -ParameterFilter { $Name -eq 'MsiInstallation' } + } + + It 'Should check both registry paths and return MSI if found in WOW6432Node' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'MSI' + } + } + + Context 'Check both uninstall registry paths for exe' { + BeforeAll { + Mock Get-CimInstance { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ItemProperty { + throw [System.Management.Automation.ItemNotFoundException]::new() + } -ParameterFilter { $Name -eq 'MsiInstallation' } + Mock Get-ItemProperty { + [PSCustomObject]@{ + UninstallString = 'C:\Program Files (x86)\TeamViewer\uninstall.exe /quiet' + } + } -ParameterFilter { $Name -eq 'UninstallString' } + Mock Get-ChildItem { + $registryKey = New-Object PSObject + $registryKey | Add-Member -MemberType NoteProperty -Name 'Name' -Value 'TeamViewer' + $registryKey | Add-Member -MemberType NoteProperty -Name 'PSPath' -Value 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\TeamViewer' + $registryKey | Add-Member -MemberType ScriptMethod -Name 'GetValue' -Value { return 'TeamViewer' } + $registryKey + } + Mock Test-Path { $true } + } + + It 'Should check both 64-bit and 32-bit uninstall paths and return exe' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'exe' + } + } + + Context 'MSI installation with MSIInstallation value of 0' { + BeforeAll { + Mock Get-CimInstance { + throw [System.Management.Automation.ItemNotFoundException]::new() + } + Mock Get-ItemProperty { + [PSCustomObject]@{ MsiInstallation = 0 } + } -ParameterFilter { $Name -eq 'MsiInstallation' } + Mock Get-ChildItem { + $registryKey = New-Object PSObject + $registryKey | Add-Member -MemberType NoteProperty -Name 'Name' -Value 'TeamViewer' + $registryKey | Add-Member -MemberType NoteProperty -Name 'PSPath' -Value 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\TeamViewer' + $registryKey | Add-Member -MemberType ScriptMethod -Name 'GetValue' -Value { return 'TeamViewer' } + $registryKey + } + Mock Get-ItemProperty { + [PSCustomObject]@{ + UninstallString = 'C:\Program Files\TeamViewer\uninstall.exe' + } + } -ParameterFilter { $Name -eq 'UninstallString' } + Mock Test-Path { $true } + } + + It 'Should return exe when MsiInstallation is 0 (exeInstallation)' { + $result = Get-TeamViewerInstallationType + $result | Should -Be 'exe' + } + } +}