Deployment: TeamViewer Host

Skill Level: Advanced

Introduction

TeamViewer Host is a cloud-based remote connectivity solution which is a specialized version of the TeamViewer remote desktop software designed for unattended access to computers or devices. 

Despite being a great tool, deployment is easy to get wrong. I have seen folks deploy the MSI, then deploy the EXE which effectively installs TeamViewer Host twice. Then you have the issue of new builds unable to uninstall older builds and confusion about applying the configuration.

I really don’t like to be judgmental, but don’t waste your time trying to follow their guides, because not only do they appear outdated, they fail miserably to describe how to properly deploy or upgrade their software.

Enough of that. Let’s get into the solution.

Solution

When I came onboard with my current employer, I was tasked with upgrading all of our endpoints to the latest TeamViewer Host, and as I was dealing with it I came across quite a few unexpected issues:

  • The latest build could not uninstall a large percentage of the legacy versions we had throughout our environment.
  • Loads of devices had two versions of the Host installed.
  • Our cloud configurations were failing to apply.

After many failed attempts, I decided to capture the steps the MSI made when upgrading to the latest build, roll my own uninstaller, and deploy the win32 app instead. Let’s take a look at this monstrosity.

static void Main(string[] args)
{
	hostName = System.Environment.MachineName;
	joinedDomain = System.Environment.UserDomainName;
	HideConsole(Console.Title);
	RemoveDeviceFromTVList(hostName);
	UninstallTeamViewer();
	InstallUpdate();
	Thread.Sleep(10000);
	AddDomain(hostName, joinedDomain);
}

Notice how odd this looks. I hide the console, remove the device from our TeamViewer contact list, uninstall the current build(s), then install the update, wait 10 seconds, then add in the alias with domain.

private static void RemoveDeviceFromTVList(string host)
{
	StartProcess("powershell.exe", "-ExecutionPolicy Bypass -NonInteractive -WindowStyle Hidden -File " + System.Environment.CurrentDirectory + "\\Remove-DeviceFromTVList.ps1 -alias " + host);
}

Strangely, I found that if I didn’t remove the device from our TeamViewer contact list before upgrading, then the cloud-based configurations I set in the portal would not get applied after the upgrade!

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

foreach($device in $devices)
{
	If($device.alias -like "$($alias)*")
	{
		$ID = $device.device_id
		Invoke-WebRequest -Uri "Https://webapi.teamviewer.com/api/v1/devices/$ID" -Method Delete -Headers $header
	}
}

So, I wrote this companion script to go with my uninstaller and call it from my uninstaller. Utilizing the TeamViewer API, I search for my device by alias, locate it’s device_id, then delete it from the contact list.

Full Example: Remove-DeviceFromTVList.ps1

Next, we will take a look at the uninstall function.

private static void UninstallTeamViewer()
{
	StopProcess("TeamViewer");
	StopService("TeamViewer");
	StartProcess("C:\\Program Files (x86)\\TeamViewer\\TeamViewer_Service.exe", "-uninstall");
	StartProcess("C:\\Program Files (x86)\\TeamViewer\\TeamViewer.exe", "api --uninstall");
	StartProcess("C:\\Program Files (x86)\\TeamViewer\\tv_x64.exe", "--action removeprint --event Local\\DriverInstallFinishEvent_Printer --id \"TeamViewer Printer\"");
	StartProcess("C:\\Program Files (x86)\\TeamViewer\\tv_x64.exe", "--action remove --id TEAMVIEWERVPN");
	RegistryKey SubKey = Registry.LocalMachine;
	SubKey = SubKey.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", true);
	string pCode = SearchSubKeys(SubKey, "DisplayName", "TeamViewer Host");
	if (!String.IsNullOrEmpty(pCode))
	{
		var TVUninstall = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", true);
		RemoveItem(TVUninstall, pCode);
	}
	var TVURL = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Classes", true);
	RemoveItem(TVURL, "teamviewer8");
	var TVSOFT = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node", true);
	RemoveItem(TVSOFT, "TeamViewer");
	RegistryKey SubKey2 = Registry.ClassesRoot;
	SubKey2 = SubKey2.OpenSubKey(@"Installer\Products", true);
	string pCode2 = SearchSubKeys(SubKey2, "ProductName", "TeamViewer Host");
	if (!String.IsNullOrEmpty(pCode2))
	{
		var TVUninstall2 = Registry.ClassesRoot.OpenSubKey(@"Installer\Products", true);
		RemoveItem(TVUninstall2, pCode2);
	}
	RegistryKey SubKey3 = Registry.LocalMachine;
	SubKey3 = SubKey3.OpenSubKey(@"SOFTWARE\Classes\Installer\Products", true);
	string pCode3 = SearchSubKeys(SubKey3, "ProductName", "TeamViewer Host");
	if (!String.IsNullOrEmpty(pCode3))
	{
		var TVUninstall3 = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Classes\Installer\Products", true);
		RemoveItem(TVUninstall3, pCode3);
	}

	try
	{
		Directory.Delete(@"C:\Program Files (x86)\TeamViewer", true);
	}
	catch (Exception ex)
	{
		Log("ERROR", ex.Message);
	}
	File.Delete(@"C:\Users\Public\Desktop\TeamViewer.lnk");
	File.Delete(@"C:\Users\Public\Desktop\TeamViewer Host.lnk");
}

As I mentioned before, I captured those steps from the TeamViewer Host installer while it was in the process of upgrading itself and I translated those actions into C# logic. The result is what you see above.

private static void InstallUpdate()
{
StartProcess(System.Environment.CurrentDirectory + "\\Teamviewer_Host.exe", "/S /norestart");
}

Next, I install the update using the win32 version of the custom module.

private static void AddDomain(string host, string domain)
{
StartProcess("C:\\Program Files (X86)\\TeamViewer\\TeamViewer.exe", "assign --api-token {API_TOKEN} --group-id {GROUP_ID} --grant-easy-access --reassign --alias \"" + host + " (" + domain + ")\"");
}

Finally, after a 10 second wait, I call the newly updated TeamViewer.exe to add the device back to the contact list and turn on easy access.

Full Example: TeamViewer.Upgrader.cs

I recommend compiling this using the .NET 3.5 framework and then making that a dependency in your Intune deployment. It’s pretty easy to toggle on using DISM, but compile it using whatever framework you desire.

Conclusion

I hope this has helped some of you with getting a handle on your TeamViewer Host deployments. There is no reason to use C#, you could re-write this in pure PowerShell if you like. Just follow the same steps, tweak what you need to make it work for you, and test until you have a solid solution!

If you have any comments or suggestions on how I can improve this guide, then please leave those in the comments section below.

Regards,

James Everett

Leave a comment