Skip to content
The Lazy Administrator
  • Home
  • Disclaimer
  • Contact
  • About Me
  • Search Icon

The Lazy Administrator

Finding ways to do the most work with the least effort possible

Deploy ConnectWise Automate (Formerly LabTech) Agent Remotely and Quietly with PowerShell

Deploy ConnectWise Automate (Formerly LabTech) Agent Remotely and Quietly with PowerShell

April 30, 2019 Brad Wyatt Comments 1 comment

Table of Contents

  • Features
    • PSJobs
    • Continuous Loop
    • Test Connection
    • Check Installation
    • Silent Install
    • Logging
  • Deployment
  • Source

There are many different ways to deploy an RMM agent to monitor end-user machines and servers. A popular option is Group Policy, also some RMM tools have the ability to do a subnet scan, but I wanted to try and do it with PowerShell while also ensuring I do not have any false positives. I wanted PowerShell to do the following tasks:

  1. Check if a machine is online
  2. Check if WinRM is available and configured correctly
  3. Check if LabTech was already installed
  4. Copy install file locally
  5. Install program
  6. Check if program installed
  7. Keep note of the installed machine

Below is a basic tree overview of the runtime flow.

The script would also log the data so I can let it run without monitoring it. It would also run in a loop, therefore if a machine was not on at a specific time, the script would end up coming back and checking if it was online at a later date. I needed to add this to account for remote users and users coming and going at different times.

The shell will also display its current status. Below is a screenshot of the script parsing machines in Active Directory.

Features

PSJobs

You can split the script into different PowerShell jobs. This is beneficial for larger organizations as you can run multiple PSJobs in parallel, therefore installing the agent on multiple machines at one given time. You can split the computer array up giving each PSJob a different set of machines to hit.

Continuous Loop

The script will loop until the number of machines installed is equal to the total number of machines it was originally given. In 99% of cases, this will never hit 100% and that is by design. I usually let this run over the course of a week (depending on the size of the environment). By letting it run for a long period of time against a number of machines, I can make sure I am hitting users that may be on vacation, sick, out of the office at the initial run, etc. If you just were to run it once, you will only be able to hit the machines that are there and turned on at that specific time. In most cases, this is not feasible. The script will also not try to install on a machine if it’s already installed so we don’t need to worry about issues arising from double installs.

Test Connection

To check if a machine is available on the network, PowerShell will test WinRM by using the Test-WSMan cmdlet. The Test-WSMan cmdlet submits an identification request that determines whether the WinRM service is running on a local or remote computer. PowerShell uses WinRM to invoke commands on a remote machine, establish a remote PSSession, and copy files over.

Check Installation

To prevent double installs or attempts to install the program when its already present, the script will parse the registry of the remote machine to see if LabTech is already present.

Silent Install

The labtech install MSI allows for the quiet switch. By using /q the install will be completely silent to the end user.

Logging

The script will log every step of the way for each machine. Lets you check which machines the agent is already installed on, which machines it couldn’t install on, and more.

Deployment

The LabTech MSI installation file that will be deployed to all my enterprise machines is stored at, “C:\transfer\LabTech_Install.msi” which is stored in the variable, “$FiletoTransfer”. You can change the MSI file name but I recommend keeping everything as C:\Transfer. The script will take the MSI in our local C:\Transfer and move it to the target machine at C:\Transfer as well.

Next, my outputted log file will be located at “C:\Automation\”, and stored in the variable, “$LogFile”.

In the script we can see it create our transfer file on the target machine:

Invoke-Command -Session $session -ScriptBlock { New-Item -ItemType Directory "C:\Transfer" -ErrorAction SilentlyContinue } | Out-Null

Below is the script block that will copy the local LabTech MSI file to our newly created C:\Transfer directory on the target machine:

Copy-Item -Path $FiletoTransfer -Destination "\\$computer\C$\Transfer\" -Force -ErrorAction Continue

To specify certain target machines you can either filter through Active Directory, import names from a file (txt, csv, etc.) or manually input them. The names are stored in an array named, “$Computers”. In my example I am grabbing all machine names from a certain Active Directory OU:

$Computers = (Get-ADComputer -Filter * -SearchBase "OU=Machines,OU=Chicago,DC=BWYA77,DC=COM").Name

Source

As always, I have the source files hosted in GitHub which I recommend going to first, but you can also download the source code below.

#Local files to transfer over so it will run locally
#If you change the msi file, change lines 87, 95
$FiletoTransfer = "C:\transfer\LabTech_Install.msi"

#I want to have some basic logging because it will run unattended 
$LogFile = "C:\Automation\"
Write-Host "Checking Log File location" -ForegroundColor White
$CheckLogDir = Test-Path $LogFile -ErrorAction SilentlyContinue
If ($CheckLogDir -eq $False)
{
	Write-Host "Not found! - Creating!"
	New-Item -ItemType Directory -Path $LogFile -Force
}
Else
{
	Write-Host "Log file path is already present, continuing "
}

#Parse computers from Active Directory
$Computers = (Get-ADComputer -Filter * -SearchBase "OU=Machines,OU=Chicago,DC=BWYA77,DC=COM").Name

#Manually enter in computers
#$Computers = "IT40", "IT45"

[int]$computerwork = 0


#Counts the number of computers it will have to hit, this tells the script when to stop once it hits this number of computers
$ComputerCount = ($Computers).count


# Get Start Time so we can output how long it takes the script to run
$startDTM = (Get-Date)
Do
{
	ForEach ($Computer in $Computers)
	{
		[int]$Retry = 0
		[int]$InstallCode = 0
		[int]$RetryCopyFile = 0
		[int]$CopyCode = 0
		
		#Test WinRM, if this fails we can't do shit
		Write-Host "Testing WSMAN Connection to $Computer" -ForegroundColor Yellow -BackgroundColor Blue
		$Heartbeat = (Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue)
		If (!$Heartbeat)
		{
			"WinRM appears to be off for $Computer" | Out-File $LogFile\log.txt -Append -Force
			Write-Host "$Computer is not able to be connected to via WinRM" -ForegroundColor Red

		}
		Else
		{
			Write-Host "WinRM appears to be open for $Computer" -ForegroundColor White
					
			#Runs the exe in silent mode. Please note that when PowerShell runs the .exe file you wont see it if youre logged in as a user anyways because it wont launch it in an interactive login by default
			
			Write-Host "Creating a new PSSession to $Computer"
			$session = New-PSSession -ComputerName $computer -ErrorAction SilentlyContinue
			If ($null -ne $Session)
			{
				Write-Host "Creating a new PSDrive on $Computer" -ForegroundColor Yellow
				Invoke-Command -Session $session -ScriptBlock { New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR } | Out-Null
				
				Write-Host "Checking to see if LabTech is installed" -ForegroundColor Yellow
				$Check = Invoke-Command -Session $session -ScriptBlock { (Get-ChildItem "HKCR:\Installer\Products") | Where-Object { $_.GetValue("ProductName") -like "*LabTech*" } }
				If ($null -ne $Check)
				{
					Write-Host "$Computer has LabTech Installed!" -ForegroundColor Yellow
					"$Computer already had it installed" | Out-File $LogFile\log.txt -Append -Force
					#incriments to keep track of the amount of computers that have it installed already 
				}
				Else
				{
					Write-Host "$Computer does not currently have LabTech installed! Continuing" -ForegroundColor Green
					Write-Host "Creating C:\Transfer\ on $Computer" -ForegroundColor Yellow
					#Creates a directory on the remote machine 
					Invoke-Command -Session $session -ScriptBlock { New-Item -ItemType Directory "C:\Transfer" -ErrorAction SilentlyContinue } | Out-Null
					Write-Host "Done!" -ForegroundColor White
					Do
					{
						Write-Host "Copying over the Windows Agent File to $Computer..." -ForegroundColor Yellow
						#Copys over the file to our new directory we created above
						Copy-Item -Path $FiletoTransfer -Destination "\\$computer\C$\Transfer\" -Force -ErrorAction Continue
						Write-Host "Done!" -ForegroundColor White
						
						$CheckforFile = Invoke-Command -Session $session -ScriptBlock { Test-Path -Path C:\transfer\LabTech_Install.msi }
						If ($CheckforFile -eq $True)
						{
							$CopyCode++
							Do
							{
								"Installing LabTech on $Computer" | Out-File $LogFile\log.txt -Append -Force
								Write-Host "Installing the agent on $Computer..." -ForegroundColor Yellow
								Invoke-Command -Session $session -ScriptBlock { Start-Process "msiexec.exe" -ArgumentList "/i C:\Transfer\LabTech_Install.msi /q" -Wait }
								
								Write-Host "Checking to see if LabTech is installed" -ForegroundColor Yellow
								$Check = Invoke-Command -Session $session -ScriptBlock { (Get-ChildItem "HKCR:\Installer\Products") | Where-Object { $_.GetValue("ProductName") -like "*LabTech*" } }
								if ($null -ne $Check)
								{
									"LabTech installed on $Computer" | Out-File $LogFile\log.txt -Append -Force
									Write-Host "$Computer has $LabTech Installed!" -ForegroundColor Green
									#Adds 1 to the variable to keep track of how many computers don't have the path and will be worked on
									$ComputerWork++
									
									$InstallCode++
								}
								Else
								{
									$Retry++
									"Could not install LabTech on $Computer" | Out-File $LogFile\log.txt -Append -Force
									Write-Host "Install Failed" -ForegroundColor Red
									#Adds 1 to the variable to keep track of how many computers don't have the path and will be worked on
									If ($Retry -eq 1)
									{
										"Retrying install of LabTech on $Computer" | Out-File $LogFile\log.txt -Append -Force
										Write-Host "Retrying install of LabTech on $Computer" -ForegroundColor Red
									}
								}
							}
							Until (($Retry -gt 3) -or ($InstallCode -gt 0))
							
							Write-Host "Exiting pssession" -ForegroundColor Yellow
							Get-PSSession -Name $Session.Name | Remove-PSSession -ErrorAction SilentlyContinue
							
						}
						Else
						{
							$RetryCopyFile++
							"Could not copy install files to $Computer" | Out-File $LogFile\log.txt -Append -Force
							Write-Host "Could not copy install files to $Computer" -ForegroundColor red
							If ($RetryCopyFile -eq 1)
							{
								"Retrying to copy install files to $Computer" | Out-File $LogFile\log.txt -Append -Force
								Write-Host "Retrying to copy install files to $Computer" -ForegroundColor red
							}
						}
					}
					Until (($RetryCopyFile -gt 3) -or ($CopyCode -gt 0))
				}
			}
			Else
			{
				"Could not establish a PSSession to $Computer" | Out-File $LogFile\log.txt -Append -Force
				Write-Host "Could not establish a PSSession to $Computer!" -ForegroundColor red
			}
			
		}
		Write-Host "Removing any ghost PSSessions" -ForegroundColor DarkYellow
		Get-PSSession | Remove-PSSession -ErrorAction SilentlyContinue
	}
}
#Run the script until we install agent on all of the computers
Until ($ComputerWork -eq $ComputerCount)
# Get End Time
$endDTM = (Get-Date)

Write-Host "---------STATS----------" -ForegroundColor White
Write-Host "SCRIPT RUNTIME: $(($endDTM - $startDTM).totalseconds) seconds" -ForegroundColor Green
Write-Host "COMPUTERS INSTALLED SUCESSFULLY: $computerwork" -ForegroundColor Green
Brad Wyatt
Brad Wyatt

My name is Bradley Wyatt; I am a 5x Microsoft Most Valuable Professional (MVP) in Microsoft Azure and Microsoft 365. I have given talks at many different conferences, user groups, and companies throughout the United States, ranging from PowerShell to DevOps Security best practices, and I am the 2022 North American Outstanding Contribution to the Microsoft Community winner.


LabTech, PowerShell
Connectwise, LabTech, PowerShell

Post navigation

PREVIOUS
Master User Creator [PowerShell GUI Software] v2 Update
NEXT
Find Un-Migrated Phone Numbers in Cisco Call Manager to Port to Skype for Business / Teams

One thought on “Deploy ConnectWise Automate (Formerly LabTech) Agent Remotely and Quietly with PowerShell”

  1. NW says:
    April 1, 2021 at 10:44 am

    This is fantastic. I might recommend that you integrate with the https://github.com/LabtechConsulting/LabTech-Powershell-Module/blob/master/LabTech/Install-LTService.md

    This will also check for .net 3.5 etc. But it does not deploy across the network like this.

    Automate sucks at deploying their own agent 🙂

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Subscribe

Email


Categories

  • Active Directory (8)
  • AI (3)
  • API (1)
  • AutoPilot (2)
  • Azure (15)
  • Bicep (4)
  • Connectwise (1)
  • Defender for Cloud Apps (1)
  • Delegated Admin (1)
  • DevOps (6)
  • Graph (6)
  • Intune (15)
  • LabTech (1)
  • Microsoft Teams (6)
  • Office 365 (19)
  • Permissions (2)
  • PowerShell (50)
  • Security (1)
  • SharePoint (3)
  • Skype for Business (1)
  • Terraform (1)
  • Uncategorized (2)
  • Yammer (1)

Recent Comments

  • Kristopher Gates on Getting Started with GitHub Copilot in the CLI
  • MD SHARIQUE AKHTAR on Modern Active Directory – An update to PSHTML-AD-Report
  • TommyBoich on How The ConnectWise Manage API Handles Pagination with PowerShell
  • LOTTERY 365 LOGIN on Windows LAPS Management, Configuration and Troubleshooting Using Microsoft Intune
  • SPRUNKI PHASE 6 on Get a New Computer’s Auto Pilot Hash Without Going Through the Out of Box Experience (OOBE)

1,739,418 People Reached

© 2025   All Rights Reserved.