Sunday, May 5, 2013

Deploy an application using a Powershell script.

If besides running the setup program there is some additional stuff you need to do before or after the actual setup, creating a Powershell script that does this job for you certainly is a good idea. Sure you can do almost everything with a batch file but... Powershell is the way to go nowadays! However, there are a few pitfalls when trying to make this work using Powershell. So let's get started...

Say you want to deploy Winzip. Winzip uses the Winzip.wzmul registration file which must be copied to the Windows common appdata folder.

First thing you will have to do is define a variable which holds the path to where the script is located. If you would run the setup from a network location there is actually no need to do this but if you use a software deployment system which runs the installation from a client's local cache you will need to know the path to where the setup files are located. Note that in batch you would use the build in variable "%~dp0"

$ScriptDirectory = split-path -parent $MyInvocation.MyCommand.Definition

Next you will have to define a variable which holds the arguments to pass to msiexec.exe. In Powershell you cannot simply enter the command as you would in cmd and expect it to work... this has to do with how Powershell parses the command line.

The actual working cmd command to install Winzip (in my environment) looks like this:

msiexec /i winzip140.msi /q ADDDESKTOPICON=0 ADDSTARTMENU=0 INSTALLCMD="/noc4u /notip /autoinstall" /l* "%LOGDIR%/Winzip14.log" (note that in my environment we use an env. variable named LOGDIR)

The trick here is to put each part as an element in an array and escape the quotes if present. To make our script reusable we place as much as possible in variables:

$Application = "`"$ScriptDirectory\winzip140.msi`""
$InstallDir = "`"C:\Program Files (x86)\WinZip`""
$logfile = "`"$env:LOGDIR\Winzip14.log`"" #Notice I use the Powershell env provider instead of the system env. variable here

$Arguments = @("/i", $Application, "/qb!", "INSTALLDIR=$InstallDir", "ADDDESKTOPICON=0", "ADDSTARTMENU=0", "/qb!", "INSTALLCMD=`"/noc4u /notip /autoinstall`"", "/l*", $logfile )

Notice the backticks... We need to escape the inner quotes! If you do not have an env. variable LOGDIR, create one or change $env:Logdir to the path where you wish to save the log file.

Next we can start the actual installation. We can do this by using the Start-Process cmdlet which has the very convenient parameter "-Wait" that allows us to wait until the process completes before the script continues. While we are here let's retrieve the Exitcode as well.

$Install = Start-Process -FilePath msiexec.exe -ArgumentList $Arguments -wait -PassThru
$Exitcode = $Install.ExitCode

Next we can perform the additional actions... in this case copy a file over. We can use the Try/Catch statements here to change the Exitcode and append the error to the log file in case an error should occur performing the additional action(s).

Try {
Copy-Item -Path "$ScriptDirectory\WinZip.wzmul" -Destination c:\Programdata\Winzip\
}
Catch {
$Exitcode = "1604"
Add-Content $logfile.Substring(1,$logfile.Length-2) $error[0]
}

Besides returning the Exitcode that is about all there is to make a basic Powershell script to deploy an application.

The complete script looks like this:

Begin {
$ErrorActionPreference = "STOP"
$ScriptDirectory = split-path -parent $MyInvocation.MyCommand.Definition

$Application = "$ScriptDirectory\winzip140.msi"
$InstallDir = "`"C:\Program Files (x86)\WinZip`""
$logfile = "`"$env:LOGDIR\Winzip14.log`""
$Arguments = @("/i", $Application, "/qb!", "INSTALLDIR=$InstallDir", "ADDDESKTOPICON=0", "ADDSTARTMENU=0", "/qb!", "INSTALLCMD=`"/noc4u /notip /autoinstall`"", "/l*", $logfile )
}

Process {
    $Install = Start-Process -FilePath msiexec.exe -ArgumentList $Arguments -wait -PassThru
    $Exitcode = $Install.ExitCode

    If ($Exitcode -eq "0") #which means the setup was successful
    {
    Try {
        Copy-Item -Path "$ScriptDirectory\WinZip.wzmul" -Destination c:\Programdata\Winzip\
        }
    Catch {
        $Exitcode = "1604"
        Add-Content $logfile.Substring(1,$logfile.Length-2) $error[0] #If you change $logfile to a path where you do not need the backticks you can drop the Substring method part.
        }
    }
}

End {
return $Exitcode
}

Modify the script to your needs and save it (as well as the Winzip.wzmul file in this case) in the same directory as your setup files and deploy with whatever deployment system you are using.

P.S. You will have to sign the script or set the Powershell execution policy to unrestricted (if company policy allows you to)

Grts.




My Blog List