08 April 2015

SCCM PowerShell SCUP Alternative

I have wanted to be able to update an application on the fly without having to create new application packages in SCCM. This is especially useful for applications that are updated quite frequently, such as Java Runtime Environment. This can be done by using the custom script detection method. The following script I wrote will go out and extract the product code from the MSI installer located in the source installation directory. It then looks at the HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{GUID} to make sure the application is installed. If that registry key is missing, SCCM will begin installation of the new software.

To make this work correctly, you will need to use the Uninstall-MSIbyName function included in my Deployment Module and listed below. Some installers uninstall the old version, but for best results, I always make sure I perform the uninstall before the install takes place. This will allow for the install script to uninstall the old version and then the installer can come back and install the new version. This function allows for you to input just a portion of the application name listed in Add/Remove programs. An example is Java. In my Java installer, I use the line below which uninstalls all Java version 8 packages:

Uninstall-MSIByName -ApplicationName "Java 8" -Switches "/qb- /norestart"

Next, you will need to rename the MSI installer to a generic name. I called mine JREx86.msi and JREx64.msi. When a new version comes out, I delete these and rename the new version to the same name. The powershell installation package will always look for the same named installer. Once you replace the MSI files, all that is required is to go into the SCCM console, select the deployment, and click Update Content. Once the content is updated on the distribution points and systems begin running the Application Deployment Evaluation Cycle, the package will begin installing the new version of the software.

This script only covers installations involving MSI files. I am also going to write a detection method to cover EXE files too. 

 Function Uninstall-MSIByName {  
      <#  
      .SYNOPSIS  
           Uninstall-MSIByName  
      .DESCRIPTION  
           Uninstalls an MSI application using the MSI file  
      .EXAMPLE  
           Uninstall-MSIByName -ApplicationName "Adobe Reader" -Switches "/qb- /norestart"  
      #>  
   
      Param([String]$ApplicationName, [String]$Switches)  
      $Uninstall = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall -Recurse -ea SilentlyContinue  
      $Uninstall += Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall -Recurse -ea SilentlyContinue  
      $SearchName = "*"+$ApplicationName+"*"  
      $Executable = $Env:windir+"\system32\msiexec.exe"  
      Foreach ($Key in $Uninstall) {  
           $TempKey = $Key.Name -split "\\"  
           If ($TempKey[002] -eq "Microsoft") {  
                $Key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"+$Key.PSChildName  
           } else {  
                $Key = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"+$Key.PSChildName  
           }  
           If ((Test-Path $Key) -eq $true) {  
                $KeyName = Get-ItemProperty -Path $Key  
                If ($KeyName.DisplayName -like $SearchName) {  
                     $TempKey = $KeyName.UninstallString -split " "  
                     If ($TempKey[0] -eq "MsiExec.exe") {  
                          $Output = "Uninstall "+$KeyName.DisplayName+"....."  
                          Write-Host "Uninstall"$KeyName.DisplayName"....." -NoNewline  
                          $Parameters = "/x "+$KeyName.PSChildName+[char]32+$Switches  
                          $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Parameters -Wait -Passthru).ExitCode  
                          If (($ErrCode -eq 0) -or ($ErrCode -eq 3010) -or ($ErrCode -eq 1605)) {  
                               $Output = $Output+"Success"  
                               Write-Host "Success" -ForegroundColor Yellow  
                          } else {  
                               $Output = $Output+"Failed with error code "+$ErrCode  
                               Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
                               $Global:Errors++  
                          }  
                          Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force  
                     }  
                }  
           }  
      }  
 }  
   


 <#  
 .SYNOPSIS  
   SCCM Application Detection for Java Runtime Environment  
 .DESCRIPTION  
   This script will detect if the latest version of Java is installed by performing a query  
   on the MSI for the product key. It then searches the uninstall registry keys for a matching  
   GUID. If it does match, then the script returns a 0 back to SCCM. If it does not match,  
   then the script does not return any code back to SCCM.  
 .Author  
   Mick Pletcher  
 .Date  
   07 April 2015  
 #>  
   
 $Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
 $Architecture = $Architecture.OSArchitecture  
 If ($Architecture -eq "32-bit") {  
      [string]$MSIPath = "\\FileShare\Oracle\JavaTest\JREx86.msi"  
 } else {  
      [string]$MSIPath = "\\FileShare\Oracle\JavaTest\JREx64.msi"  
 }  
 #"ProductCode","ProductVersion","ProductName"  
 [string]$Property = "ProductCode"  
 $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer  
 $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase","InvokeMethod",$Null,$WindowsInstaller,@($MSIPath,0))  
 $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"  
 $View = $MSIDatabase.GetType().InvokeMember("OpenView","InvokeMethod",$null,$MSIDatabase,($Query))  
 $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)  
 $Record = $View.GetType().InvokeMember("Fetch","InvokeMethod",$null,$View,$null)  
 $Value = $Record.GetType().InvokeMember("StringData","GetProperty",$null,$Record,1)  
 If ($Architecture -eq "32-bit") {  
      $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"+$Value  
 } else {  
      $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"+$Value  
 }  
 If ((Test-Path $RegPath) -eq $true) {  
      Return 0  
 } else {}  
   

0 comments:

Post a Comment