Public/Service/Start-CWAA.ps1

function Start-CWAA {
    <#
    .SYNOPSIS
        Starts the ConnectWise Automate agent services.
    .DESCRIPTION
        Verifies that the Automate agent services (LTService, LTSvcMon) are present. Checks
        for any process using the LTTray port (default 42000) and kills it. If a
        protected application holds the port, increments the TrayPort (wrapping from
        42009 back to 42000). Sets services to Automatic startup and starts them via
        sc.exe. Waits up to one minute for LTService to reach the Running state, then
        issues a Send Status command for immediate check-in.
    .EXAMPLE
        Start-CWAA
        Starts the ConnectWise Automate agent services.
    .EXAMPLE
        Start-CWAA -WhatIf
        Shows what would happen without actually starting the services.
    .NOTES
        Author: Chris Taylor
        Alias: Start-LTService
    .LINK
        https://github.com/christaylorcodes/ConnectWiseAutomateAgent
    #>

    [CmdletBinding(SupportsShouldProcess = $True)]
    [Alias('Start-LTService')]
    Param()

    Begin {
        Write-Debug "Starting $($MyInvocation.InvocationName)"
        # Identify processes that are using the tray port
        [array]$processes = @()
        $Port = (Get-CWAAInfo -EA 0 -Verbose:$False -WhatIf:$False -Confirm:$False -Debug:$False | Select-Object -Expand TrayPort -EA 0)
        if (-not ($Port)) { $Port = '42000' }
        $startedSvcCount = 0
    }

    Process {
        if (-not (Get-Service 'LTService', 'LTSvcMon' -ErrorAction SilentlyContinue)) {
            if ($WhatIfPreference -ne $True) {
                Write-Error "Services NOT Found."
            }
            else {
                Write-Error "What If: Services NOT Found."
            }
            return
        }
        Try {
            if ((('LTService') | Get-Service -EA 0 | Where-Object { $_.Status -eq 'Stopped' } | Measure-Object | Select-Object -Expand Count) -gt 0) {
                Try { $netstat = & "$env:windir\system32\netstat.exe" -a -o -n 2>'' | Select-String -Pattern " .*[0-9\.]+:$($Port).*[0-9\.]+:[0-9]+ .*?([0-9]+)" -EA 0 }
                Catch { Write-Debug 'Failed to call netstat.exe.'; $netstat = $null }
                Foreach ($line in $netstat) {
                    $processes += ($line -split ' {4,}')[-1]
                }
                $processes = $processes | Where-Object { $_ -gt 0 -and $_ -match '^\d+$' } | Sort-Object | Get-Unique
                if ($processes) {
                    Foreach ($processId in $processes) {
                        Write-Output "Process ID:$processId is using port $Port. Killing process."
                        Try { Stop-Process -Id $processId -Force -Verbose -EA Stop }
                        Catch {
                            Write-Warning "There was an issue killing process: $processId"
                            Write-Warning "This generally means that a 'protected application' is using this port."
                            # TrayPort wraps within the 42000-42009 range. If a protected process holds
                            # the current port, increment and wrap back to 42000 after 42009.
                            $newPort = [int]$Port + 1
                            if ($newPort -gt 42009) { $newPort = 42000 }
                            Write-Warning "Setting tray port to $newPort."
                            New-ItemProperty -Path $Script:CWAARegistryRoot -Name TrayPort -PropertyType String -Value $newPort -Force -WhatIf:$False -Confirm:$False | Out-Null
                        }
                    }
                }
            }
            if ($PSCmdlet.ShouldProcess('LTService, LTSvcMon', 'Start Service')) {
                $Script:CWAAServiceNames | ForEach-Object {
                    if (Get-Service $_ -EA 0) {
                        Set-Service $_ -StartupType Automatic -EA 0 -Confirm:$False -WhatIf:$False
                        $Null = & "$env:windir\system32\sc.exe" start "$($_)" 2>''
                        if ($LASTEXITCODE -ne 0) {
                            Write-Warning "sc.exe start returned exit code $LASTEXITCODE for service '$_'."
                        }
                        $startedSvcCount++
                        Write-Debug "Executed Start Service for $($_)"
                    }
                }

                # Wait for services if we issued start commands
                $stoppedServiceCount = ('LTService') | Get-Service -EA 0 | Where-Object { $_.Status -ne 'Running' } | Measure-Object | Select-Object -Expand Count
                if ($stoppedServiceCount -gt 0 -and $startedSvcCount -eq 2) {
                    $timeout = New-TimeSpan -Minutes 1
                    $stopwatch = [Diagnostics.Stopwatch]::StartNew()
                    Write-Verbose 'Waiting for services to start...'
                    Do {
                        Start-Sleep 2
                        $stoppedServiceCount = ('LTService') | Get-Service -EA 0 | Where-Object { $_.Status -ne 'Running' } | Measure-Object | Select-Object -Expand Count
                    } Until ($stopwatch.Elapsed -gt $timeout -or $stoppedServiceCount -eq 0)
                    $stopwatch.Stop()
                    Write-Verbose 'Service start wait completed.'
                }

                # Report final state
                if ($stoppedServiceCount -eq 0) {
                    Write-Output 'Services started successfully.'
                    Write-CWAAEventLog -EventId 2000 -EntryType Information -Message 'Agent services started successfully.'
                    $Null = Invoke-CWAACommand 'Send Status' -EA 0 -Confirm:$False
                }
                elseif ($startedSvcCount -gt 0) {
                    Write-Output 'Service Start was issued but LTService has not reached Running state.'
                    Write-CWAAEventLog -EventId 2001 -EntryType Warning -Message 'Agent services failed to reach Running state after start.'
                }
                else {
                    Write-Output 'Service Start was not issued.'
                }
            }
        }
        Catch {
            Write-Error "There was an error starting the Automate agent services. $_"
            Write-CWAAEventLog -EventId 2002 -EntryType Error -Message "Agent service start failed. Error: $($_.Exception.Message)"
        }
    }

    End {
        Write-Debug "Exiting $($MyInvocation.InvocationName)"
    }
}