Community FAQ
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
duster1
Engaged Sweeper III

I've redone my deployment for Zoom and wanted to share it with the community.

1. First we need to make the powershell script.

Save as Install-Zoom.ps1 or something similar.

# Install-Zoom.ps1
$ErrorActionPreference = 'Stop'

$Url = "https://zoom.us/client/latest/ZoomInstallerFull.msi?archType=x64"

# AU2 policies via zConfig
$zConfig = @"
IntegrateZoomWithOutlook=1;
AU2_EnableAutoUpdate=true;
AU2_SetUpdateChannel=0;
AU2_InstallAtIdleTime=true;
AU2_SafeUpgradePeriod=0000-0600;
AU2_EnableShowZoomUpdates=false;
AU2_EnableUpdateAvailableBanner=false;
AU2_EnableManualUpdate=false;
"@ -replace "(\r|\n)","" -replace "\s+",""

# Save location requested
$DownloadDir = "C:\temp"
New-Item -ItemType Directory -Path $DownloadDir -Force | Out-Null
$MsiPath = Join-Path $DownloadDir "ZoomInstallerFull_x64.msi"

$LogDir  = Join-Path $env:ProgramData "ZoomDeploy"
$LogPath = Join-Path $LogDir "ZoomInstall.log"
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null

# TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

function Get-ActiveSessionIds {
  # Returns an array of active session IDs (best-effort)
  $ids = @()
  try {
    $lines = (quser 2>$null)
    foreach ($line in $lines) {
      # Look for "Active" lines; session id is typically the 3rd token (varies slightly)
      if ($line -match '\s+Active\s+') {
        $tokens = ($line -replace '^\s+','') -split '\s+'
        # Common formats:
        # USERNAME  SESSIONNAME  ID  STATE  ...
        # USERNAME              ID  STATE  ...
        foreach ($t in $tokens) {
          if ($t -match '^\d+$') { $ids += [int]$t; break }
        }
      }
    }
  } catch {}
  return ($ids | Select-Object -Unique)
}

function Send-UserMessage {
  param([Parameter(Mandatory=$true)][string]$Message)
  try {
    & msg.exe * /TIME:60 $Message | Out-Null
  } catch {}
}

function Stop-ZoomIfRunning {
  # Try graceful close first, then force
  $procs = Get-Process -Name "Zoom" -ErrorAction SilentlyContinue
  if (-not $procs) { return $false }

  Send-UserMessage "Zoom is updating. Please save your work. Zoom will close briefly."

  foreach ($p in $procs) {
    try {
      if ($p.MainWindowHandle -ne 0) {
        [void]$p.CloseMainWindow()
      }
    } catch {}
  }

  Start-Sleep -Seconds 10

  # Re-check and force kill any remaining
  $still = Get-Process -Name "Zoom" -ErrorAction SilentlyContinue
  if ($still) {
    foreach ($p in $still) {
      try { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue } catch {}
    }
    # Extra belt-and-suspenders (kills child processes too)
    try { & taskkill.exe /IM zoom.exe /T /F | Out-Null } catch {}
  }

  return $true
}

function Get-MsiProperty {
  param(
    [Parameter(Mandatory=$true)][string]$Path,
    [Parameter(Mandatory=$true)][string]$Property
  )
  $installer = New-Object -ComObject WindowsInstaller.Installer
  $db = $installer.OpenDatabase($Path, 0)
  $view = $db.OpenView("SELECT Value FROM Property WHERE Property='$Property'")
  $view.Execute()
  $record = $view.Fetch()
  $value = $record.StringData(1)
  $view.Close()
  return $value
}

function Normalize-Version {
  param([string]$v)
  if ([string]::IsNullOrWhiteSpace($v)) { return $null }
  $parts = ($v -replace '[^\d\.]','').Split('.', [System.StringSplitOptions]::RemoveEmptyEntries)
  if ($parts.Count -eq 0) { return $null }
  $parts = $parts | Select-Object -First 4
  while ($parts.Count -lt 4) { $parts += '0' }
  try { return [version]($parts -join '.') } catch { return $null }
}

function Get-InstalledZoomVersion {
  $paths = @(
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
    "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
  )

  $candidates = foreach ($p in $paths) {
    Get-ItemProperty $p -ErrorAction SilentlyContinue |
      Where-Object {
        $_.DisplayName -and
        (
          $_.DisplayName -match '^Zoom( Workplace)?( \(64-bit\))?$' -or
          $_.DisplayName -match 'Zoom Workplace' -or
          $_.DisplayName -match 'Zoom Meetings'
        )
      } |
      Select-Object DisplayName, DisplayVersion, Publisher
  }

  if (-not $candidates) { return $null }

  $best = $candidates |
    Sort-Object @{
      Expression = { if ($_.Publisher -match 'Zoom') { 0 } else { 1 } }
    }, @{
      Expression = { Normalize-Version $_.DisplayVersion }
      Descending = $true
    } |
    Select-Object -First 1

  return $best.DisplayVersion
}

Write-Host "Downloading Zoom x64 MSI (latest) to $MsiPath ..."
try {
  Start-BitsTransfer -Source $Url -Destination $MsiPath -ErrorAction Stop
}
catch {
  Invoke-WebRequest -Uri $Url -OutFile $MsiPath -UseBasicParsing
}

# Verify signature
$sig = Get-AuthenticodeSignature -FilePath $MsiPath
if ($sig.Status -ne 'Valid') {
  throw "Downloaded MSI signature not valid. Status=$($sig.Status)"
}

$availableVersionRaw = Get-MsiProperty -Path $MsiPath -Property "ProductVersion"
$availableVersion = Normalize-Version $availableVersionRaw
if (-not $availableVersion) { throw "Could not parse downloaded MSI ProductVersion: '$availableVersionRaw'" }

$installedVersionRaw = Get-InstalledZoomVersion
$installedVersion = Normalize-Version $installedVersionRaw

Write-Host "Installed Zoom version : $installedVersionRaw"
Write-Host "Available Zoom version : $availableVersionRaw"

if ($installedVersion -and ($installedVersion -eq $availableVersion)) {
  Write-Host "Zoom already current. Skipping install."
  Remove-Item $MsiPath -Force -ErrorAction SilentlyContinue
  exit 0
}

# Close Zoom if running (with user notification)
[void](Stop-ZoomIfRunning)

Write-Host "Installing/Updating Zoom..."
$args = @(
  "/i", "`"$MsiPath`"",
  "/qn", "/norestart",
  "/log", "`"$LogPath`"",
  "zConfig=`"$zConfig`""
)

$proc = Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -PassThru

if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) {
  Write-Host "Install failed; leaving MSI in place for troubleshooting: $MsiPath"
  throw "Zoom install failed. ExitCode=$($proc.ExitCode). Log=$LogPath"
}

Write-Host "Zoom install complete. ExitCode=$($proc.ExitCode)."

# Success message
Send-UserMessage "Zoom update complete. Please open Zoom from the desktop shortcut."

# Cleanup after success
Remove-Item $MsiPath -Force -ErrorAction SilentlyContinue
Write-Host "Removed installer: $MsiPath"

exit $proc.ExitCode

2. Save the script in your packageshare\Scripts folder.

3. Modify the zconfig items as needed for your scenario. View zooms resources for the AU2 policies.

4. Update/create your deployment.

This will be a Command action for the deployment:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{PackageShare}\Scripts\Install-Zoom.ps1"

Note: Change servername to the name of your server or use 


What does the script do?

  • Downloads the latest 64-bit Zoom MSI from Zoom to C:\temp\ZoomInstallerFull_x64.msi (forces TLS 1.2, uses BITS and falls back to Invoke-WebRequest).

  • Verifies the download is legitimately signed (Authenticode signature must be Valid) and reads the MSI’s ProductVersion.

  • Detects the currently installed Zoom version by checking uninstall registry entries, normalizes versions, and skips installation if it’s already up to date.

  • If an update is needed, it warns the user, then closes Zoom if it’s running (tries graceful close, then force-kills as needed).

  • Installs/updates Zoom silently via msiexec /qn and writes an install log to C:\ProgramData\ZoomDeploy\ZoomInstall.log, applying several Zoom auto-update/policy settings via zConfig (enable auto-update, hide update UI/banners, disable manual update, set safe upgrade period, integrate with Outlook, etc.).

  • On success, it notifies the user that the update is complete and cleans up the MSI; on failure, it leaves the MSI for troubleshooting and throws an error.

0 REPLIES 0

Deployment Packages

Share topics/ issues related to deployment packages. Please use/rely on content with caution as it is publicly generated.

New to Lansweeper?

Try Lansweeper For Free

Experience Lansweeper with your own data.
Sign up now for a 14-day free trial.

Try Now