Automate ConnectWise ScreenConnect Deployment with PowerShell

Key Takeaways

  • Automated Efficiency: The PowerShell script for installing ConnectWise ScreenConnect automates and simplifies the deployment process, enhancing operational efficiency.
  • Customizable Installation: It supports dynamic URL construction for customized installations based on parameters like organization name, location, and device type.
  • Wide Applicability: Ideal for MSPs and IT professionals managing multiple devices across various locations.
  • Advanced Features: Offers features like installation status checks and error logging, surpassing traditional manual or batch script methods.
  • Compatibility and Limitations: Works best with Windows 8 and Server 2012 onwards, with potential limitations on older versions.
  • Security Considerations: Users must review and understand the script to ensure security, especially in MSP environments.
  • NinjaOne Integration: This script aligns well with NinjaOne’s unified IT management approach, offering a cohesive solution for IT professionals.
  • Continuous Adaptation: Regular updates and modifications to the script may be required to align with the latest versions of ConnectWise ScreenConnect.

In the dynamic landscape of IT, efficiency and automation are key to maintaining a competitive edge. Scripting has emerged as a crucial tool, enabling IT professionals to streamline complex processes. The PowerShell script for installing ConnectWise ScreenConnect exemplifies this, offering a sophisticated solution for remote management and support.

Background

ConnectWise ScreenConnect, a popular remote support and management tool, is widely used by IT professionals and Managed Service Providers (MSPs). Its ability to facilitate remote access to devices is essential in today’s increasingly decentralized work environments. This PowerShell script simplifies the deployment process of ScreenConnect, addressing a common challenge faced by IT teams – efficient, scalable, and automated software installation.

The Script:

<#
.SYNOPSIS
Download and Install ConnectWise ScreenConnect from the domain used for ScreenConnect. Supports automatic customization of the device type, location and other ScreenConnect Fields.

.DESCRIPTION
Download and Install ConnectWise ScreenConnect from the domain used for ScreenConnect. Supports automatic customization of the device type, location and other ScreenConnect Fields.

.EXAMPLE
-ScreenConnectDomain "testscreen.screenconnect.com" -UseOrgName -UseLocation -UseDeviceType

Installer Log File location will be: C:WindowsTEMPtmp9A50.tmp
ScreenConnect Client (abcd123456) is not installed and will be installed.
Attempting to build from domain...
URL Built: https://testscreen.screenconnect.com/Bin/Test Company.ClientSetup.msi?e=Access&y=Guest&c=Kyle - OOB&c=Main Office&c=&c=Workstation&c=&c=&c=&c=
URL Given, Downloading the file...
Download Attempt 1
Exit Code: 0
Success

PRESET PARAMETER: -ScreenConnectDomain "your.domain.com"
Your ScreenConnect instance's domain name. The script will use this to construct a download URL from scratch with the options you selected.

PRESET PARAMETER: -UseOrgName
Modifies your URL to use the organization name in the Company Name Field in ScreenConnect.

PRESET PARAMETER: -UseLocation
Modifies your URL to use the Location Name in the Site Name Field in ScreenConnect.

PRESET PARAMETER: -UseDeviceType
Modifies your URL to include the type of device in ScreenConnect. (Server, Workstation, Laptop etc.)

PRESET PARAMETER: -Department "Your Department Name Here"
Modifies your URL to include your desired department name in ScreenConnect.

PRESET PARAMETER: -SkipSleep
By default the script sleeps for a random interval between 3 and 60 seconds prior to downloading the file. This parameter skips the sleep.

PRESET PARAMETER: -Force
If ScreenConnect is already installed attempt to install it anyways.

.NOTES
Minimum OS Architecture Supported: Windows 8, Windows Server 2012
Can work on lower versions of Windows, provided that the OS/.NET is able to download the file. PowerShell 2.0 might face issues due to its lack of TLS support in .NET 2.0.

Adapted from Chris White's script: https://ninjarmm.zendesk.com/hc/en-us/community/posts/7549797399821-Connectwise-Control-Installer

Release Notes: Initial Release
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#>
#>

[CmdletBinding()]
param (
    # Change the defaults if you don't wish to use parameters when running this script
    [Parameter()]
    [String]$MSI = "ClientSetup.msi",
    [Parameter()]
    [String]$DestinationFolder = "$env:TEMP",
    [Parameter()]
    [String]$ScreenConnectDomain,
    [Parameter()]
    [String]$InstanceID,
    [Parameter()]
    [Switch]$UseOrgName = [System.Convert]::ToBoolean($env:useNinjaOrganizationName),
    [Parameter()]
    [Switch]$UseLocation = [System.Convert]::ToBoolean($env:useNinjaLocationName),
    [Parameter()]
    [Switch]$UseDeviceType = [System.Convert]::ToBoolean($env:addDeviceType),
    [Parameter()]
    [String]$Department,
    [Parameter()]
    [Switch]$SkipSleep = [System.Convert]::ToBoolean($env:skipSleep),
    [Parameter()]
    [Switch]$Force = [System.Convert]::ToBoolean($env:force)
)
begin {
    # If Script Form is used replace the parameters with what was filled in.
    if ($env:screenconnectDomainName -and $env:screenconnectDomainName -notlike "null") { $ScreenConnectDomain = $env:screenconnectDomainName }
    if ($env:department -and $env:department -notlike "null") { $Department = $env:department }

    # Some means of installing the file is required.
    if (-not ($ScreenConnectDomain)) { Write-Error "A domain is required to install control."; exit 1 }

    if ($ScreenConnectDomain -match "^http(s)?://") {
        Write-Warning "http(s):// is not part of the domain name. Removing http(s):// from your input...."
        $ScreenConnectDomain = $ScreenConnectDomain -replace "^http(s)?://"
        Write-Warning "New Domain Name $ScreenConnectDomain."
    }

    if ($ScreenConnectDomain -match "^C:/") {
        Write-Error "It looks like you entered in a file path by mistake. We actually need the domain name used to reach your ScreenConnect website for example 'companyname.screenconnect.com'"
        exit 1
    }
    
    #### Helper functions used throughout the script ####

    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    # Extract the ProductName from the msi
    function Get-ControlPanelName {
        [CmdletBinding()]
        param (
            [Parameter()]
            [string]$msiPath
        )
        $windowsInstaller = New-Object -ComObject WindowsInstaller.Installer
        $database = $windowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $windowsInstaller, @($msiPath, 0))
        $query = "SELECT `Value` FROM `Property` WHERE `Property` = 'ProductName'"

        $view = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $database, $query)
        $view.GetType().InvokeMember("Execute", "InvokeMethod", $null, $view, $null)

        $record = $view.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $view, $null)
        if ($record) {
            return $record.GetType().InvokeMember("StringData", "GetProperty", $null, $record, 1)
        }

        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($windowsInstaller) | Out-Null
        [System.GC]::Collect()
    }

    # Is it a Server or Desktop OS?
    function Get-ProductType {
        if ($PSVersionTable.PSVersion.Major -ge 5) {
            $OS = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object ProductType -ExpandProperty ProductType
        }
        else {
            $OS = Get-WmiObject -Class Win32_OperatingSystem | Select-Object ProductType -ExpandProperty ProductType
        }
        
        return $OS
    }

    # Check the Chassis type to find out if it's a laptop or not.
    function Test-IsLaptop {
        if ($PSVersionTable.PSVersion.Major -ge 5) {
            $Chassis = Get-CimInstance -ClassName win32_systemenclosure | Select-Object ChassisTypes -ExpandProperty ChassisTypes
        }
        else {
            $Chassis = Get-WmiObject -Class win32_systemenclosure | Select-Object ChassisTypes -ExpandProperty ChassisTypes
        }

        switch ($Chassis) {
            9 { return $True }
            10 { return $True }
            14 { return $True }
            default { return $False }
        }
    }

    # Check's the two uninstall registry keys to see if the app is installed. Needs the name as it would appear in Control Panel.
    function Find-UninstallKey {
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline = $True)]
            [String]$DisplayName,
            [Parameter()]
            [Switch]$UninstallString
        )
        process {
            $UninstallList = New-Object System.Collections.Generic.List[Object]

            $Result = Get-ChildItem HKLM:SoftwareWow6432NodeMicrosoftWindowsCurrentVersionUninstall* | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
            if ($Result) { $UninstallList.Add($Result) }

            $Result = Get-ChildItem HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall* | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
            if ($Result) { $UninstallList.Add($Result) }

            # Programs don't always have an uninstall string listed here so to account for that I made this optional.
            if ($UninstallString) {
                $UninstallList | Select-Object -ExpandProperty UninstallString -ErrorAction SilentlyContinue
            }
            else {
                $UninstallList
            }
        }
    }

    # Handy download function
    function Invoke-Download {
        param(
            [Parameter()]
            [String]$URL,
            [Parameter()]
            [String]$Path,
            [Parameter()]
            [Switch]$SkipSleep
        )
        Write-Host "URL given; downloading the file..."

        $SupportedTLSversions = [enum]::GetValues('Net.SecurityProtocolType')
        if ( ($SupportedTLSversions -contains 'Tls13') -and ($SupportedTLSversions -contains 'Tls12') ) {
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol::Tls13 -bor [System.Net.SecurityProtocolType]::Tls12
        }
        elseif ( $SupportedTLSversions -contains 'Tls12' ) {
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
        }
        else {
            # Not everything requires TLS 1.2, but we'll try anyways.
            Write-Warning "TLS 1.2 and or TLS 1.3 isn't supported on this system. This download may fail!"
            if ($PSVersionTable.PSVersion.Major -lt 3) {
                Write-Warning "PowerShell 2 / .NET 2.0 doesn't support TLS 1.2."
            }
        }

        $i = 1
        While ($i -lt 4) {
            if (-not ($SkipSleep)) {
                $SleepTime = Get-Random -Minimum 3 -Maximum 30
                Start-Sleep -Seconds $SleepTime
            }

            Write-Host "Download Attempt $i"

            try {
                $WebClient = New-Object System.Net.WebClient
                $WebClient.DownloadFile($URL, $Path)
                $File = Test-Path -Path $Path -ErrorAction SilentlyContinue
            }
            catch {
                Write-Warning "An error has occurred while downloading!"
                Write-Warning $_.Exception.Message
            }

            if ($File) {
                $i = 4
            }
            else {
                $i++
            }
        }

        if (-not (Test-Path $Path)) {
            Write-Error "Failed to download file!"
            Exit 1
        }
    }

    # This will build our screenconnect download url if only given a domain name or if modification is needed to include the device type, location, org name etc.
    function Build-URL {
        param(
            [Parameter()]
            [String]$BaseURL,
            [Parameter()]
            [String]$Domain,
            [Parameter()]
            [String]$MSI,
            [Parameter()]
            [String]$Department,
            [Parameter()]
            [Switch]$UseOrgName,
            [Parameter()]
            [Switch]$UseLocation,
            [Parameter()]
            [Switch]$UseDeviceType
        )

        Write-Host "Attempting to build from domain..."
        $URL = "https://$Domain/Bin/$env:NINJA_COMPANY_NAME.ClientSetup.msi`?e=Access&y=Guest"

        if ($UseOrgName) { $URL = $URL + "&c=$env:NINJA_ORGANIZATION_NAME" }else { $URL = $URL + "&c=" }
        if ($UseLocation) { $URL = $URL + "&c=$env:NINJA_LOCATION_NAME" }else { $URL = $URL + "&c=" }
        if ($Department) { $URL = $URL + "&c=$Department" }else { $URL = $URL + "&c=" }
        if ($UseDeviceType) {
            switch (Get-ProductType) {
                1 { if (Test-IsLaptop) { $URL = $URL + "&c=Laptop&c=&c=&c=&c=" }else { $URL = $URL + "&c=Workstation&c=&c=&c=&c=" } }
                2 { $URL = $URL + "&c=Domain Controller&c=&c=&c=&c=" }
                3 { $URL = $URL + "&c=Server&c=&c=&c=&c=" }
            }
        }
        else {
            $URL = $URL + "&c=&c=&c=&c=&c="
        }

        Write-Host "URL Built: $URL"

        return $URL
    }

    if (-not (Test-IsElevated)) {
        Write-Error -Message "Access Denied. Please run with Administrator privileges."
        exit 1
    }

    if (-not (Test-Path $DestinationFolder -ErrorAction SilentlyContinue)) {
        Write-Host "Destination Folder does not exist! Creating directory..."
        New-Item $DestinationFolder -ItemType Directory
    }

    #Set the log file as a temporary file, it will be created in the temp folder of the context the script runs in (c:windowstemp or c:usersusernameappdatatemp)
    $InstallerLogFile = [IO.Path]::GetTempFileName()
    Write-Host "Installer Log File location will be: $InstallerLogFile"
}
process {
    # Arguments required to download the file
    $DownloadArgs = @{ Path = "$DestinationFolder$MSI" }
    if ($SkipSleep) { $DownloadArgs["SkipSleep"] = $True }

    # Build the arguments needed to create the url
    $ArgumentList = @{ Domain = $ScreenConnectDomain }
    if ($UseOrgName) { $ArgumentList["UseOrgName"] = $True }
    if ($UseLocation) { $ArgumentList["UseLocation"] = $True }
    if ($UseDeviceType) { $ArgumentList["UseDeviceType"] = $True }
    if ($Department) { $ArgumentList["Department"] = $Department }

    # Build the URL and get it ready for download
    $DownloadArgs["URL"] = Build-Url @ArgumentList

    # Download the installer
    Invoke-Download @DownloadArgs

    # Grab the installer file
    $InstallerFile = Join-Path -Path $DestinationFolder -ChildPath $MSI -Resolve

    # Define the name of the software we are searching for and look for it in both the 64 bit and 32 bit registry nodes
    $ProductName = "$(Get-ControlPanelName -msiPath $InstallerFile)".Trim()
    if (-not $ProductName) { 
        Write-Error "Failed to fetch the product name from the MSI at path '$InstallerFile'. Ensure the MSI path is correct and the MSI contains the necessary product information."
        exit 1
    }

    # If already installed, exit.
    $IsInstalled = Find-UninstallKey -DisplayName $ProductName
    if ($IsInstalled -and -not ($Force)) {
        Write-Host "$ProductName is already installed; exiting..."
        exit 0
    }

    # ScreenConnect install arguments
    $Arguments = "/c msiexec /i ""$InstallerFile"" /qn /norestart /l ""$InstallerLogFile"" REBOOT=REALLYSUPPRESS"

    # Install and let the user know the exit code
    $Process = Start-Process -Wait cmd -ArgumentList $Arguments -PassThru
    Write-Host "Exit Code: $($Process.ExitCode)";

    # Interpret the exit code
    switch ($Process.ExitCode) {
        0 { Write-Host "Success" }
        3010 { Write-Host "Success. Reboot required to complete installation" }
        1641 { Write-Host "Success. Installer has initiated a reboot" }
        default {
            Write-Error "Exit code does not indicate success"
            Get-Content $InstallerLogFile -ErrorAction SilentlyContinue | Select-Object -Last 50 | Write-Host
        }
    }

    exit $Process.ExitCode
    
}
end {
    
    
    
}

 

Access over 300+ scripts in the NinjaOne Dojo

Get Access

Detailed Breakdown

The script operates in a straightforward manner:

  • Parameter Initialization: It begins by defining parameters like MSI file name, destination folder, and ScreenConnect domain.
  • Pre-Execution Checks: It checks for administrator privileges and the existence of the destination folder.
  • Dynamic URL Building: A significant feature is its ability to dynamically construct a download URL based on parameters like organization name, location, and device type.
  • Download Process: The script then downloads the ScreenConnect installer from the constructed URL.
  • Installation: Post-download, it checks if ScreenConnect is already installed. If not, or if the Force parameter is used, it proceeds with the installation.
  • Logging: Throughout the process, logs are maintained for troubleshooting.

Potential Use Cases

Imagine an MSP responsible for managing IT infrastructure across multiple client sites. Manually installing ScreenConnect on each device would be time-consuming. With this script, the MSP can automate installations, customizing settings like location and device type for each client, thereby saving time and reducing errors.

Comparisons

Traditionally, software installation might involve manual downloading and setting up on each device or using basic batch scripts without customization capabilities. This PowerShell script surpasses such methods by offering advanced features like dynamic URL construction and installation status checks, leading to more efficient and error-free deployments.

FAQs

Q: Is the script compatible with all Windows versions? 
A: It supports Windows 8 and Server 2012 onwards, though older versions may work with some limitations.

Q: Can it handle installations on a large scale? 
A: Yes, it’s designed for scalability and can handle installations across numerous devices.

Q: Is it secure to use this script? 
A: The script is secure, but always review and understand any script before running it in your environment.

Implications

While this script significantly streamlines the installation process, users must be aware of security implications. Incorrect use or modification might lead to vulnerabilities, especially in MSP environments where a single breach can impact multiple clients.

Recommendations

  • Review and Test: Always thoroughly review and test the script in a controlled environment.
  • Customize with Care: Modify the script parameters according to your specific requirements.
  • Stay Updated: Keep abreast of updates to ConnectWise ScreenConnect and adjust the script as necessary.

Final Thoughts

NinjaOne, with its focus on unified IT operations, complements such scripting solutions. It offers a platform where such scripts can be integrated and managed efficiently, further enhancing the IT management capabilities of professionals. This PowerShell script, along with tools like NinjaOne, demonstrates how automation and smart tools are indispensable in modern IT management.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service deliver tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Categories:

You might also like

×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our Terms of Use:

  • Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
  • Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
  • Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library belonging to or under the control of any other software provider.
  • Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
  • Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
  • Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
  • EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).