Scripting the workstation upgrade

I've been upgrading environments for years via batch files.  I think it's time I ported one of these into a Powershell script.  Batch files just seem so antiquated.

My batch script is broken down as follows:

  1. Script, Log File, and Machine Preparation
  2. Uninstall old products
  3. Install new product
  4. Clean the workstation and user profiles
  5. Apply any tweaks

Script, Log File, and Machine Preparation

First I need to identify where the logging information from the upgrade should be placed.  In this example I'll place it into the temporary path as defined in the environment variables.  This might not work for me if deploying administratively (retrieving those files requires you know which account executed the script), but it works for my example.

$logDir = Join-Path $env:TEMP "TRIM_Upgrade"
$logFile = Join-Path $logDir "$((get-date).ToString('yyyyMMdd')).log"
if ( (Test-Path $logFile) -eq $false ) 
    New-Item -ItemType File -Force -Path $logFile
    Write-Host "Created Log File: $($logFile)"
} else {
    Write-Host "Log File Already Existed: $($logFile)"

Uninstall old products

Most products can be uninstalled by executing "msiexec /X{<GUID>}", where <GUID> is the GUID of the application.  These are super easy to find in the registry when inspecting the environment to be upgraded. With those in hand I can sequence their removal by manually invoking the command whilst supplying the GUID.  Obviously an important thing to consider is how to react when one of the products is missing (is that an upgrade failure?), but one easy way to accomplish this is shown below.

$uninstalls = @( 'D8B2D69F-7FA0-4BC8-8E31-C675162229D1', 'B480C3B2-9432-41B9-BD4A-421A4A6AB4C6', '112268A9-B0FB-421C-BEDB-A08B32E84207' )
foreach ( $uninstall in $uninstalls ) 
    if ( (Start-Process -FilePath "msiexec.exe" -ArgumentList "/X{$($uninstall)} /passive /norestart" -Wait -Passthru).ExitCode -eq 0 ) {
        Write-Host "Uninstalled: $($uninstall)"
    } else {
        Write-Host "Not Uninstalled: $($uninstall)"

Install new Product

To install the new version we need to execute the same command, but with different parameters.  One parameter will be the source installer, another will be where to log MSIEXEC output, and the rest will provide values to necessary fields used by the installer.

$msiFile =  'HPE_CM_x86.msi'
$installerLog = Join-Path $logDir ($msiFile+'.log')
$args =  """$($msiFile)"" /qb /norestart /l*vx ""$($installerLog)"" INSTALLDIR=""C:\Program Files\Hewlett Packard Enterprise\Content Manager\"" ADDLOCAL=HPTRIM,Client HPTRIMDIR=""C:\HPE Content Manager"" DEFAULTDBNAME=""CM"" DEFAULTDB=""CM"" STARTMENU_NAME=""HPE Content Manager"" TRIM_DSK=""1"" TRIMREF=""TRIM"" PRIMARYURL=""WG1:1137"" SECONDARYURL=""TRIMWG2:1137"" AUTOGG=""1"" WORD_ON=""0"" EXCEL_ON=""0"" POWERPOINT_ON=""0"" PROJECT_ON=""0"" OUTLOOK_ON=""1"" AUTHMECH=""0"""
if ( (Start-Process -FilePath 'msiexec.exe' -ArgumentList $args -Wait -Passthru).ExitCode -eq 0 ) {
    Write-Host "Installed: $($msiFile)"
} else {
    Write-Host "Installation Failed: $($msiFile)"

Don't forget that HPE now released Content Manager patches using the MSP approach, so after executing this you'd also the apply the patches in the same manner.  Same thing goes for any add-ons. 

Since this is now in Powershell, I could actually implement a commandlet with commandbinding such that I remotely perform the installation.  Implementing the script that way means you don't even really need to execute it via some central administrative location.  Anyone with access to powershell, with permission to install software, and with remote pssession access to the target machine can perform an upgrade via the script.  That is something batch files could never do.

Cleaning up the Workstation

The installation process leaves all kinds of things on the machine.  Some are okay and some are not.  Ultimately the cleanup and tweaking tasks are geared towards ensuring the end-user's experience is desirable (or at least acceptable).  

A prime example is the dreaded lingering, broken application shortcut:

Last icon shown above is a broken link, which uninstall cannot remove

Last icon shown above is a broken link, which uninstall cannot remove

When the uninstall happens from the previous version, it has no capability to go into each user's profile and remove any links they have manually pinned within the operating system.  That includes the desktop, start menu, the task bar, and quick access area of explorer.  Why not remove those as part of the upgrade?

$profiles = (gwmi win32_userprofile | select LocalPath)
foreach ( $profile in $profiles ) 
    $pinnedStartMenu = Join-Path $profile.LocalPath "AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\StartMenu\TRIM.lnk"
    if ( (Test-Path $pinnedStartMenu) -eq $true ) 
        Remove-Item $pinnedStartMenu -Force
        Write-Host "Removed: $($pinnedStartMenu)"

Applying Tweaks

Every piece of software has bugs.  During an upgrade it's important to overcome those bugs by implementing any fixes or tweaks required.  For instance, one of the CM builds searched for the existence of dictionary files in an incorrect spot. If left unresolved then no one would be able to use the spellchecker.  By handling that issue in the upgrade script we didn't let the bug impact usage of the product.