fredag den 30. september 2011

Installshield MSI Multiple Instances

While working on automating installation of SuperOffice Customer Service (formerly known as eJournal ) I ran into a few issues. I started with just copying all files and changing configuration files but it didn’t quite do the trick. Doing installation of Customer Service it also install's 2 Windows Services and scripting that would just not be worth it, so I decided to see if I could control the installer instead.

If you have installed SuperOffice Customer Service once, the next time you call SuperOffice.CustomerService.exe you will get a prompt to either create a new instance or modify an existing installation

image

I’m no expert on MSI installers, but I did some digging around and it seems the way they get that to work, is doing some Transform scripting inside the installer. (that’s why it is an exe and not an MSI )

The first thing I did was extract the MSI and have a look at the properties in the installer so I could pass on the correct parameters. but when I called MSIEXEC with the SMI, I would get all kinds of different errors. (makes sense now ). After a lot of goggling I found that I could control the instance by number, by adding /Instance= to the .EXE … test that I created a couple in instances like this

SuperOffice.CustomerService.exe /instance=1 /V"EJHOSTNAME=servicesotest1.so7.wingu.dk INSTALLDIR=C:\servicesotest1.so7.wingu.dk /L*v c:\1.log /passive"
SuperOffice.CustomerService.exe /instance=2 /V"EJHOSTNAME=servicesotest2.so7.wingu.dk INSTALLDIR=C:\servicesotest2.so7.wingu.dk /L*v c:\2.log /passive"

And uninstalling

SuperOffice.CustomerService.exe /x /instance=1 /V" /L*v c:\1.log /passive"
SuperOffice.CustomerService.exe /x /instance=2 /V" /L*v c:\2.log /passive"

so I though that would be awesome, I could just pass on my CustomerService ID as Instance Name and everything would be perfect. But no … if I pass on a number higher than 1000 I fails and doesn’t work, so I had to do some more digging. I decided to see if I could emulate all the instance numbers and find a “free” instance number when installing, and the same, when uninstalling and look at install location.

I came across this neat little PowerShell Snap In/Module to get information about installed software. but you can also get all the information with WMI if you prefer that kind of stuff

$programs_installed = @{};
$win32_product = @(get-wmiobject -class 'Win32_Product' )
foreach ($product in $win32_product) {
$product | fl
# $product.InstallLocation
$name = $product.Name;

$version = $product.Version;
if ($name -ne $null) {
$programs_installed.$name = $version;
}
}

So now I can easely get a list of installed instances including my “instance number” by running


Get-MSIProductInfo -Name 'SuperOffice Customer Service' | fl ProductName, ProductCode, InstallLocation, PackageCode, InstanceType, Transforms

For a complete list, do


Get-MSIProductInfo | fl AuthorizedLUAApp, InstallContext, InstalledProductName, Language, Name, PackageCode, PackageName, ProductIcon, Transforms, Version, CollectUserInfo, Equals, GetComponentState, GetFeatureState, GetHashCode, GetType, ToString, PSPath, Item, AdvertisedLanguage, AdvertisedPackageCode, AdvertisedPackageName, AdvertisedPerMachine, AdvertisedProductIcon, AdvertisedProductName, AdvertisedTransforms, AdvertisedVersion, Context, Features, HelpLink, HelpTelephone, InstallDate, InstallLocation, InstallSource, IsAdvertised, IsElevated, IsInstalled, LocalPackage, PrivilegedPatchingAuthorized, ProductCode, ProductId, ProductName, ProductVersion, Publisher, RegCompany, RegOwner, SourceList, UrlInfoAbout, UrlUpdateInfo, UserSid, Advertised, Installed, V1, AssignmentType, DiskPrompt, InstanceType, LastUsedSource, LastUsedType, MediaPackagePath, ProductState, VersionMajor, VersionMinor, VersionString

So to wrap it all up ….


function InstallSuperOfficeCustomerService([String]$fqdn){
$InstanceID = 0
#$AllreadyInstalled = Get-MSIProductInfo -Name 'SuperOffice Customer Service'
$AllreadyInstalled = @(get-wmiobject -class 'Win32_Product' ) | where {$_.Name -eq 'SuperOffice Customer Service'}
if($AllreadyInstalled){
# add 1, to avoid getting "default" instance, and check if its free
$InstanceID = $InstanceID + 1
$isFree = $AllreadyInstalled | where { $_.Transforms -eq ":InstanceId$InstanceID.mst" }
# If not free, loop til we find one unused
while($isFree){
$InstanceID = $InstanceID + 1
$isFree = $AllreadyInstalled | where { $_.Transforms -eq ":InstanceId$InstanceID.mst" }
}
}
if(! (Test-Path 'c:\CustomerService')){ $temp = New-Item 'c:\CustomerService' -type directory }
# replace /passive with /qn to hide the UI
$parameters = "/instance=$InstanceID /V""EJHOSTNAME=$fqdn INSTALLDIR=C:\CustomerService\$fqdn /L*v c:\CustomerService\install-$fqdn.log /passive"""
# Write-Host ("X:\SuperOfficeInstall\Customer Service 7.0 SR2\SuperOffice.CustomerService.exe " + $parameters)
$installStatement = [System.Diagnostics.Process]::Start( "X:\SuperOfficeInstall\Customer Service 7.0 SR2\SuperOffice.CustomerService.exe", $parameters )
$installStatement.WaitForExit()
}

function UnInstallSuperOfficeCustomerService([String]$fqdn){
# now i could look though the instance number and call it with
# SuperOffice.CustomerService.exe /x /instance=$InstanceID /V" /L*v c:\$InstanceID.log /passive"
# but instead i'll play it safe and use the ProductCode

#$isInstalled = Get-MSIProductInfo -Name 'SuperOffice Customer Service' | where {$_.InstallLocation -like "*$fqdn*" }
$isInstalled = @(get-wmiobject -class 'Win32_Product' ) | where {($_.Name -eq 'SuperOffice Customer Service') -and ($_.InstallLocation -like "*$fqdn*")}
if($isInstalled){
# replace /passive with /qn to hide the UI
# $ProductCode = $isInstalled.ProductCode
$ProductCode = $isInstalled.IdentifyingNumber
$parameters = "/x $ProductCode /passive /L*v c:\CustomerService\uninstall-$fqdn.log "
# Write-Host ("msiexec " + $parameters)
$uninstallStatement = [System.Diagnostics.Process]::Start( "msiexec", $parameters )
$uninstallStatement.WaitForExit()
}
}

function IsSuperOfficeCustomerServiceInstalled([String]$fqdn){
$isInstalled = @(get-wmiobject -class 'Win32_Product' ) | where {($_.Name -eq 'SuperOffice Customer Service') -and ($_.InstallLocation -like "*$fqdn*")}
if($isInstalled){
return $true
}
else {
return $false
}
}

InstallSuperOfficeCustomerService 'deleteme4.domain.com'

UnInstallSuperOfficeCustomerService 'deleteme4.domain.com'

IsSuperOfficeCustomerServiceInstalled 'deleteme4.domain.com'
IsSuperOfficeCustomerServiceInstalled 'deleteme5.domain.com'

Ingen kommentarer:

Send en kommentar