Deploy ConnectWise Automate (Formerly LabTech) Agent Remotely and Quietly with PowerShell
Table of Contents
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:
- Check if a machine is online
- Check if WinRM is available and configured correctly
- Check if LabTech was already installed
- Copy install file locally
- Install program
- Check if program installed
- 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
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.
One thought on “Deploy ConnectWise Automate (Formerly LabTech) Agent Remotely and Quietly with PowerShell”
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 🙂