PowerShell – Script to install Adobe Acrobat Reader

Just another Tech site

PowerShell – Script to install Adobe Acrobat Reader

Script to install and Detects Acrobat Reader (32/64), stops processes/services, installs a provided 32-bit installer silently, disables auto-update via registry policy, removes desktop shortcuts, and logs to C:\logs\<date>\acrobats.log

<#
.SYNOPSIS
  Detects Acrobat Reader (32/64), stops processes/services, installs a provided 32-bit installer silently,
  disables auto-update via registry policy, removes desktop shortcuts, and logs to C:\logs\<date>\acrobats.log

.NOTES
  - PowerShell 5.1
  - Must run as Administrator
  - Provide path to the 32-bit installer via -InstallerPath (EXE or MSI)
  - Test in a safe environment before wide use
#>

$InstallerPath = $PSScriptRoot

param(
    [Parameter(Mandatory=$true)]
    [string]$InstallerPath
)

# -------------------------
# Elevation check
# -------------------------
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Error "This script must be run as Administrator."
    exit 1
}

# -------------------------
# Logging helpers
# -------------------------
function Get-LogFile {
    $date = (Get-Date).ToString('yyyy-MM-dd')
    $dir = Join-Path -Path 'C:\logs' -ChildPath $date
    if (-not (Test-Path $dir)) {
        New-Item -Path $dir -ItemType Directory -Force | Out-Null
    }
    return Join-Path -Path $dir -ChildPath 'acrobat-manage.log'
}

$LogFile = Get-LogFile

function Log {
    param(
        [string]$Message,
        [ValidateSet("INFO","WARN","ERROR","DEBUG")] [string]$Level = "INFO"
    )
    $ts = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
    $line = "$ts [$Level] $Message"
    try {
        $line | Out-File -FilePath $LogFile -Encoding UTF8 -Append
    } catch {
        Write-Warning "Failed to write log: $($_.Exception.Message)"
    }
    Write-Host $line
}

# -------------------------
# Utility: parse uninstall registry entries and detect Acrobat Reader
# -------------------------
function Get-AcrobatReaderEntries {
    <#
    Returns PSCustomObject entries with:
      DisplayName, UninstallString, InstallLocation, Version, RegistryKey, Architecture
    #>
    $results = @()

    $regRoots = @(
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
        "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
        "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
    )

    foreach ($root in $regRoots) {
        try {
            $keys = Get-ChildItem -Path $root -ErrorAction SilentlyContinue
            foreach ($k in $keys) {
                try {
                    $props = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
                    if ($null -eq $props) { continue }
                    $display = $props.DisplayName
                    if ($display -and $display -match 'Adobe Acrobat Reader|Acrobat Reader') {
                        $arch = if ($root -like '*WOW6432Node*') { '32-bit' } else { '64-bit' }
                        $results += [PSCustomObject]@{
                            DisplayName     = $props.DisplayName
                            UninstallString = $props.UninstallString
                            InstallLocation = $props.InstallLocation
                            Version         = $props.DisplayVersion
                            Publisher       = $props.Publisher
                            RegistryKey     = $k.PSPath
                            Architecture    = $arch
                        }
                    }
                } catch { continue }
            }
        } catch { continue }
    }

    # Fallback: check Program Files folders
    $possible = @(
        Join-Path $env:ProgramFiles 'Adobe\Acrobat Reader*',
        Join-Path ${env:ProgramFiles(x86)} '\Adobe\Acrobat Reader*'
    )
    foreach ($p in $possible) {
        foreach ($d in Get-ChildItem -Path $p -Directory -ErrorAction SilentlyContinue) {
            if (-not ($results | Where-Object { $_.InstallLocation -and ($_.InstallLocation -eq $d.FullName) })) {
                $arch = if ($d.FullName -like '*Program Files (x86)*') { '32-bit' } else { '64-bit' }
                $results += [PSCustomObject]@{
                    DisplayName     = "Adobe Acrobat Reader (detected folder)"
                    UninstallString = $null
                    InstallLocation = $d.FullName
                    Version         = $null
                    Publisher       = "Adobe"
                    RegistryKey     = $null
                    Architecture    = $arch
                }
            }
        }
    }

    return $results
}

# -------------------------
# Stop processes and services
# -------------------------
function Stop-AcrobatProcessesAndServices {
    param([switch]$Force)

    Log "Stopping Acrobat/Adobe processes and services..." "INFO"

    $procNames = @('AcroRd32','Acrobat','AdobeARM','AdobeUpdateService','AdobeCollabSync','RdrCEF','AdobeNotificationClient') 

    foreach ($name in $procNames) {
        try {
            $procs = Get-Process -Name $name -ErrorAction SilentlyContinue
            foreach ($p in $procs) {
                try {
                    Log "Stopping process $($p.ProcessName) (Id $($p.Id))" "INFO"
                    Stop-Process -Id $p.Id -Force:$Force -ErrorAction Stop
                    Log "Stopped process Id $($p.Id)" "INFO"
                } catch {
                    Log "Failed to stop process $($p.ProcessName) Id $($p.Id): $($_.Exception.Message)" "WARN"
                }
            }
        } catch {}
    }

    # Stop Adobe services
    try {
        $svcCandidates = Get-Service | Where-Object { $_.Name -match 'Adobe|ARM|AdobeUpdate' -or $_.DisplayName -match 'Adobe' } -ErrorAction SilentlyContinue
        foreach ($svc in $svcCandidates) {
            try {
                if ($svc.Status -ne 'Stopped') {
                    Log "Stopping service $($svc.Name) ($($svc.DisplayName))" "INFO"
                    Stop-Service -Name $svc.Name -Force -ErrorAction Stop
                    Log "Stopped service $($svc.Name)" "INFO"
                }
            } catch {
                Log "Failed to stop service $($svc.Name): $($_.Exception.Message)" "WARN"
            }
        }
    } catch {
        Log "Service enumeration failed: $($_.Exception.Message)" "WARN"
    }
}

# -------------------------
# Install provided 32-bit installer silently
# -------------------------
function Install-ReaderSilently {
    param(
        [Parameter(Mandatory=$true)][string]$PathToInstaller
    )

    if (-not (Test-Path $PathToInstaller)) {
        Log "Installer not found at path: $PathToInstaller" "ERROR"
        return $false
    }

    $ext = [IO.Path]::GetExtension($PathToInstaller).ToLowerInvariant()
    Log "Installer found: $PathToInstaller (ext: $ext)" "INFO"

    try {
        if ($ext -eq '.msi') {
            # Use msiexec for MSI installers
            $args = "/i `"$PathToInstaller`" /qn /norestart"
            Log "Running MSI installer: msiexec.exe $args" "INFO"
            $p = Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -PassThru -WindowStyle Hidden
            Log "msiexec exit code: $($p.ExitCode)" "INFO"
            return ($p.ExitCode -eq 0)
        } else {
            # EXE installer - try common silent switches for Adobe Reader
            # Common Adobe Reader switch: /sAll  (silent), /rs (suppress reboot)
            # We'll try: /sAll /rs /msi /norestart (some installers accept /sAll)
            $tryArgs = @(
                "/sAll /rs",
                "/sAll /rs /msi /norestart",
                "/s /v/qn",
                "/silent"
            )

            foreach ($a in $tryArgs) {
                try {
                    Log "Attempting installer with args: $a" "INFO"
                    $proc = Start-Process -FilePath $PathToInstaller -ArgumentList $a -Wait -PassThru -WindowStyle Hidden -ErrorAction Stop
                    Log "Installer returned exit code $($proc.ExitCode) for args: $a" "INFO"
                    if ($proc.ExitCode -eq 0) {
                        Log "Installer succeeded with args: $a" "INFO"
                        return $true
                    }
                } catch {
                    Log "Installer attempt failed for args [$a]: $($_.Exception.Message)" "WARN"
                    # continue trying other switches
                }
            }

            # Last resort: run installer without args (interactive) but we avoid that in silent script
            Log "All silent attempts failed. Installer may require different switches." "ERROR"
            return $false
        }
    } catch {
        Log "Installation attempt failed: $($_.Exception.Message)" "ERROR"
        return $false
    }
}

# -------------------------
# Verify installation and folder existence
# -------------------------
function Verify-ReaderInstalled {
    param(
        [string]$ExpectedArch = "32-bit"
    )

    Log "Verifying Acrobat Reader installation (expecting $ExpectedArch)..." "INFO"
    $entries = Get-AcrobatReaderEntries
    $match = $entries | Where-Object { $_.DisplayName -and ($_.DisplayName -match 'Adobe Acrobat Reader|Acrobat Reader') -and ($_.Architecture -eq $ExpectedArch) }

    if ($match) {
        foreach ($m in $match) {
            Log "Found: $($m.DisplayName) Version: $($m.Version) InstallLocation: $($m.InstallLocation) Arch: $($m.Architecture)" "INFO"
            if ($m.InstallLocation -and (Test-Path $m.InstallLocation)) {
                Log "Install folder exists: $($m.InstallLocation)" "INFO"
            } else {
                Log "Install folder not found or not recorded for this entry." "WARN"
            }
        }
        return $true
    } else {
        Log "No matching Acrobat Reader installation found for architecture $ExpectedArch." "WARN"
        return $false
    }
}

# -------------------------
# Disable auto update via registry policy keys
# -------------------------
function Disable-AcrobatAutoUpdate {
    Log "Disabling Adobe Acrobat Reader auto-update via registry policy keys..." "INFO"

    # Paths to set policy keys for Reader DC and older versions; set both 64-bit and 32-bit (Wow6432Node)
    $policyPaths = @(
        "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown",
        "HKLM:\SOFTWARE\WOW6432Node\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown",
        "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\*",
        "HKLM:\SOFTWARE\WOW6432Node\Policies\Adobe\Acrobat Reader\*"
    )

    # Primary key/value to disable updater: bUpdater = 0 (DWORD)
    foreach ($base in @(
        "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown",
        "HKLM:\SOFTWARE\WOW6432Node\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown",
        "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\*",
        "HKLM:\SOFTWARE\WOW6432Node\Policies\Adobe\Acrobat Reader\*"
    )) {
        try {
            # Create the key if it doesn't exist (only for the DC path)
            $target = $base -replace '\*','DC\FeatureLockDown'
            if (-not (Test-Path $target)) {
                New-Item -Path $target -Force | Out-Null
                Log "Created registry key: $target" "DEBUG"
            }
            # Set bUpdater = 0
            New-ItemProperty -Path $target -Name "bUpdater" -Value 0 -PropertyType DWord -Force | Out-Null
            Log "Set bUpdater=0 at $target" "INFO"
        } catch {
            Log "Failed to set policy at $target : $($_.Exception.Message)" "WARN"
        }
    }

    # Also try to disable Adobe Update Service via service config (set startup to Disabled)
    try {
        $svc = Get-Service -Name 'AdobeARMservice' -ErrorAction SilentlyContinue
        if ($svc) {
            try {
                Set-Service -Name $svc.Name -StartupType Disabled
                Log "Set service $($svc.Name) startup type to Disabled" "INFO"
            } catch {
                Log "Failed to set service startup type: $($_.Exception.Message)" "WARN"
            }
        } else {
            # Try common service names
            foreach ($name in @('AdobeARM','AdobeUpdateService','AdobeUpdateService')) {
                $s = Get-Service -Name $name -ErrorAction SilentlyContinue
                if ($s) {
                    try {
                        Set-Service -Name $s.Name -StartupType Disabled
                        Log "Set service $($s.Name) startup type to Disabled" "INFO"
                    } catch {
                        Log "Failed to set service $($s.Name) startup type: $($_.Exception.Message)" "WARN"
                    }
                }
            }
        }
    } catch {
        Log "Service disable attempt failed: $($_.Exception.Message)" "WARN"
    }
}

# -------------------------
# Remove desktop shortcuts
# -------------------------
function Remove-AdobeDesktopShortcuts {
    <#
    Removes Adobe/Acrobat Reader shortcuts from current user and public desktops.
    Relies on an existing Log function: Log "message" "LEVEL"
    Returns a PSCustomObject summary: @{ Removed = <int>; Skipped = <int>; Errors = <int> }
    #>

    Log "Removing Adobe Reader desktop shortcuts (current user and public)..." "INFO"

    $removedCount = 0
    $skippedCount = 0
    $errorCount = 0

    # Desktop paths: current user and public (all users)
    $desktopPaths = @(
        [Environment]::GetFolderPath("Desktop"),
        [Environment]::GetFolderPath("CommonDesktopDirectory")
    ) | Where-Object { $_ -and (Test-Path $_) } | Select-Object -Unique

    # Helper to test whether a target or name indicates Adobe Reader
    $isAdobeTarget = {
        param($targetPath, $fileName)
        if ($null -ne $targetPath -and $targetPath -ne "") {
            return ($targetPath -match '(?i)AcroRd32|Acrobat|Adobe|Reader')
        }
        if ($null -ne $fileName -and $fileName -ne "") {
            return ($fileName -match '(?i)Adobe|Acrobat|Reader')
        }
        return $false
    }

    foreach ($d in $desktopPaths) {
        try {
            # Get both .lnk and .url files
            $files = Get-ChildItem -Path $d -Include *.lnk, *.url -File -ErrorAction SilentlyContinue
            foreach ($f in $files) {
                try {
                    $target = $null
                    $isAdobe = $false

                    if ($f.Extension -ieq ".lnk") {
                        try {
                            $wsh = New-Object -ComObject WScript.Shell
                            $lnk = $wsh.CreateShortcut($f.FullName)
                            $target = $lnk.TargetPath
                            $isAdobe = & $isAdobeTarget $target $f.Name
                        } catch {
                            # COM resolution failed; fall back to filename check
                            $isAdobe = & $isAdobeTarget $null $f.Name
                        }
                    } elseif ($f.Extension -ieq ".url") {
                        try {
                            # .url files are INI-like; read the URL= line
                            $content = Get-Content -Path $f.FullName -ErrorAction Stop
                            $urlLine = $content | Where-Object { $_ -match '^\s*URL\s*=' } | Select-Object -First 1
                            if ($urlLine) {
                                $target = ($urlLine -split '=',2)[1].Trim()
                                $isAdobe = & $isAdobeTarget $target $f.Name
                            } else {
                                # fallback to filename
                                $isAdobe = & $isAdobeTarget $null $f.Name
                            }
                        } catch {
                            $isAdobe = & $isAdobeTarget $null $f.Name
                        }
                    } else {
                        # unexpected extension; skip
                        $skippedCount++
                        continue
                    }

                    if ($isAdobe) {
                        try {
                            Remove-Item -Path $f.FullName -Force -ErrorAction Stop
                            Log "Removed shortcut: $($f.FullName) -> $target" "INFO"
                            $removedCount++
                        } catch {
                            Log "Failed to remove shortcut $($f.FullName): $($_.Exception.Message)" "WARN"
                            $errorCount++
                        }
                    } else {
                        $skippedCount++
                    }
                } catch {
                    Log "Error processing file $($f.FullName): $($_.Exception.Message)" "WARN"
                    $errorCount++
                }
            }
        } catch {
            Log "Failed enumerating desktop $d : $($_.Exception.Message)" "WARN"
            $errorCount++
        }
    }

    $summary = [PSCustomObject]@{
        Removed = $removedCount
        Skipped = $skippedCount
        Errors  = $errorCount
    }

    Log "Desktop shortcut removal summary Removed=$removedCount Skipped=$skippedCount Errors=$errorCount" "INFO"
    return $summary
}


# -------------------------
# Main flow
# -------------------------
Log "=== Acrobat Reader management started ===" "INFO"

# 1) Detect current installations
$entries = Get-AcrobatReaderEntries
if ($entries.Count -eq 0) {
    Log "No Adobe Acrobat Reader installations detected." "INFO"
} else {
    foreach ($e in $entries) {
        Log "Detected: $($e.DisplayName) | Version: $($e.Version) | Arch: $($e.Architecture) | InstallLocation: $($e.InstallLocation)" "INFO"
    }
}

# 2) Stop processes and services
Stop-AcrobatProcessesAndServices -Force

Start-Sleep -Seconds 2

# 3) Install provided 32-bit installer silently
$installedOk = $false
try {
    Log "Attempting silent install of provided installer: $InstallerPath" "INFO"
    $installedOk = Install-ReaderSilently -PathToInstaller $InstallerPath
    if ($installedOk) {
        Log "Installer reported success." "INFO"
    } else {
        Log "Installer did not report success. Check installer and silent switches." "ERROR"
    }
} catch {
    Log "Installation attempt threw exception: $($_.Exception.Message)" "ERROR"
}

# Wait a bit for install to complete and services/processes to settle
Start-Sleep -Seconds 8

# 4) Verify installation (expect 32-bit)
$verify = Verify-ReaderInstalled -ExpectedArch "32-bit"
if ($verify) {
    Log "Verification: Acrobat Reader 32-bit appears installed." "INFO"
} else {
    Log "Verification: Acrobat Reader 32-bit not detected after install." "WARN"
}

# 5) Disable auto update
Disable-AcrobatAutoUpdate

# 6) Remove desktop shortcuts
Remove-AdobeDesktopShortcuts

# 7) Final detection and log summary
$finalEntries = Get-AcrobatReaderEntries
if ($finalEntries.Count -eq 0) {
    Log "Final check: No Acrobat Reader entries detected." "WARN"
} else {
    foreach ($f in $finalEntries) {
        Log "Final detected: $($f.DisplayName) | Version: $($f.Version) | Arch: $($f.Architecture) | InstallLocation: $($f.InstallLocation)" "INFO"
    }
}

Log "=== Acrobat Reader management finished ===" "INFO"

# Exit with code 0 if install succeeded and verification true, else 2
if ($installedOk -and $verify) { exit 0 } else { exit 2 }