<# .SYNOPSIS This script will automatically download and extract the latest Java version and update an existing Application in SCCM. .DESCRIPTION The script will download the latest version of Java from the Internet to the local %temp%-folder, and extract the MSI-package. The version- and product code properties will be fetched from the MSI. A new folder will be created, using the version number as name. The MSI- and CAB-files will be moved to this new folder. An existing Application in SCCM will then be updated with the new version number, product code and content source path. Finally the distribution point will be updated. The script assumes that PS App Deployment Toolkit is used to deploy Java. A template folder must be specified, that contains all the PS App Deployment Toolkit files needed to deploy Java. These files will be copied to the new source folder. IMPORTANT: This script only works with Java 7 and does NOT support Java 8 yet. .NOTES Author: Joachim Bryttmar, Contribit AB .LINK Author's blog: http://www.infogeek.se .EXAMPLE .\Update-Java.ps1 -SiteCode "S01" -JavaBaseSourcePath = "\\Server\Sources\Applications\Java" -JavaTemplatePath = "\\Server\Sources\Applications\Java\Template" -ApplicationName = "Java" -DeploymentTypeName = "Java" #> #******************************************************************************************************************************************************** # PARAMETERS #******************************************************************************************************************************************************** [CmdLetBinding()] Param( $SiteCode = "S01", # The base path for all Java versions. The script will create a subfolder here, with the name of the newest Java version number. This folder will be used as content content source path. $JavaBaseSourcePath = "\\sccm01\Sources\Applications\Java", # The source to a template folder that contains the PS App Deployment Toolkit files used to deploy Java. $JavaTemplatePath = "\\sccm01\Sources\Applications\Java\Template", #This is the name the downloaded file will be given. You should leave this unchanged. $DownloadFilename = "Java.exe", # The name of the Application used to deploy Java in SCCM. $ApplicationName = "Java", # The name of the deployment type in the Application in SCCM. $DeploymentTypeName = "Java" ) #******************************************************************************************************************************************************** # MAIN SCRIPT #******************************************************************************************************************************************************** function Main { # Download Java from the Internet $JavaDownloadUrl = ((Invoke-WebRequest -Uri http://www.java.com/en/download/manual_java7.jsp).Links | Where outerText -Like "*Windows Offline (32-bit)*").href #$JavaDownloadUrl = ((Invoke-WebRequest -Uri http://www.java.com/en/download/windows_offline.jsp).Links | Where outerText -Like "*Agree and Start Free Download*").href Download-file $JavaDownloadUrl "$env:temp\" $DownloadFilename # Extract the MSI- and CAB-file from downloaded EXE-file. Extract-Java "$env:temp\" $DownloadFilename # Delete the downloaded EXE-file Remove-Item -Path "$env:temp\$DownloadFilename" # Get properties from the MSI-file. [string]$PackageName = (Split-Path -leaf $DownloadFilename) -replace ".exe",".msi" #[string]$Version = ("$env:temp\$PackageName") [string]$Version = Get-MsiProperty ("$env:temp\$PackageName") "'ProductVersion'" $Version = $Version.Trim() [string]$ProductCode = Get-MsiProperty ("$env:temp\$PackageName") "'ProductCode'" $ProductCode = $ProductCode.Trim() # Check if the source folder for the latest Java version already exists. If it does we will skip creating it and copy the files. If (-Not (Test-Path -LiteralPath "$JavaBaseSourcePath\$Version")) { # Create a new source folder New-Item -ItemType Directory -Path "$JavaBaseSourcePath\$Version" | Out-Null # Copy the Java template folder to the new source folder Copy-Item "$JavaTemplatePath\*" "$JavaBaseSourcePath\$Version" -Recurse # Move the extracted MSI- and CAB-files to the new source folder $MSIFileName = (Split-Path -leaf $DownloadFilename) -replace ".exe",".msi" Copy-Item "$env:temp\$MSIFileName" "$JavaBaseSourcePath\$Version\Files" Copy-Item "$env:temp\data1.cab" "$JavaBaseSourcePath\$Version\Files" } # Connect to CM-Site $CurrentLocation = Get-Location Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1) Set-Location ($SiteCode + ":") # Update the Detection Method, Application version and source folder Set-CMApplication -Name $ApplicationName -SoftwareVersion $Version Set-CMDeploymentType -ApplicationName "$ApplicationName" -DeploymentTypeName "$DeploymentTypeName" -ProductCode "$ProductCode" -MsiOrScriptInstaller -ContentLocation "$JavaBaseSourcePath\$Version" # Update the distribution points Update-CMDistributionPoint -ApplicationName $ApplicationName -DeploymentTypeName $DeploymentTypeName # Return to the current drive Set-Location $CurrentLocation } #******************************************************************************************************************************************************** # FUNCTIONS #******************************************************************************************************************************************************** function Download-File { Param( $Url, $Path, $Filename ) $WebClient = New-Object System.Net.WebClient $WebClient.DownloadFile($Url,$Path + $Filename) } # This function will extract the MSI-file from the downloaded exe-file. # Credits to Remko Weijnen at https://www.cupfighter.net/2014/01/100-automation-of-java-updates for the code function Extract-Java { Param( $Path, $Filename ) $Definition = @" using System; using System.Runtime.InteropServices; namespace Java { public class Installer { public static int JavaCabId = 0x66; public static int JavaMsiStaticId = 0x67; public static int JavaMsiId = 0x65; // Java stores the resources in a named resource type JAVA_INSTALLER private static string JavaInstaller = "JAVA_INSTALLER"; public static bool ExtractFile(string dllName, int id, string fileName) { IntPtr hModule; IntPtr lpJavaInstaller; IntPtr hResource; IntPtr hGlobal; IntPtr Buffer; uint dwSize; // Load file as dll but only for resource loading hModule = LoadLibraryEx(dllName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); if (hModule == IntPtr.Zero) { return false; } try { // Get pointer to resource type string lpJavaInstaller = (IntPtr)Marshal.StringToHGlobalAnsi(JavaInstaller); // Find the resource location hResource = FindResource(hModule, (IntPtr)id, lpJavaInstaller); // We can release the pointer to the resource type string now Marshal.FreeHGlobal(lpJavaInstaller); if (hResource == IntPtr.Zero) { return false; } // Get resource size dwSize = SizeofResource(hModule, hResource); if (dwSize > 0) { // Get a handle to the first byte of the resource hGlobal = LoadResource(hModule, hResource); if (hGlobal == IntPtr.Zero) { return false; } // Get a pointer to the resource Buffer = LockResource(hGlobal); if (Buffer == IntPtr.Zero) { return false; } // Copy the resource to a byte array byte[] b = new byte[dwSize]; Marshal.Copy(Buffer, b, 0, b.Length); // Save the byte array to disk System.IO.File.WriteAllBytes(fileName, b); } } finally { // Unload FreeLibrary(hModule); } return true; } [DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint = "LoadLibrary", SetLastError = true)] private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPTStr)]string lpFileName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr hModule); private static uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr LoadLibraryEx([MarshalAs(UnmanagedType.LPTStr)]string lpFileName, IntPtr hReservedNull, UInt32 dwFlags); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); [DllImport("kernel32.dll", SetLastError = true)] private static extern uint SizeofResource(IntPtr hModule, IntPtr hResource); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResource); [DllImport("Kernel32.dll", EntryPoint = "LockResource")] private static extern IntPtr LockResource(IntPtr hGlobal); } } "@ Add-Type -TypeDefinition $Definition If (Test-Path -LiteralPath $Path) { $PackageName = (Split-Path -leaf $Filename) -replace ".exe",".msi" [Java.Installer]::ExtractFile($Path + $Filename, [Java.Installer]::JavaCabId, $Path + "\data1.cab") | Out-Null [Java.Installer]::ExtractFile($Path + $Filename, [Java.Installer]::JavaMsiStaticId, $Path + "\" + $PackageName) | Out-Null } } function Get-MsiProperty { Param( $Path, $Property ) function Get-Property ($Object, $PropertyName, [object[]]$ArgumentList) { return $Object.GetType().InvokeMember($PropertyName, 'Public, Instance, GetProperty', $null, $Object, $ArgumentList) } function Invoke-Method ($Object, $MethodName, $ArgumentList) { return $Object.GetType().InvokeMember($MethodName, 'Public, Instance, InvokeMethod', $null, $Object, $ArgumentList) } $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest $msiOpenDatabaseModeReadOnly = 0 $Installer = New-Object -ComObject WindowsInstaller.Installer $Database = Invoke-Method $Installer OpenDatabase @($Path, $msiOpenDatabaseModeReadOnly) $View = Invoke-Method $Database OpenView @("SELECT Value FROM Property WHERE Property=$Property") Invoke-Method $View Execute $Record = Invoke-Method $View Fetch if ($Record) { Write-Output (Get-Property $Record StringData 1) } Invoke-Method $View Close @() Remove-Variable -Name Record, View, Database, Installer } #******************************************************************************************************************************************************** # Start the main script #******************************************************************************************************************************************************** Main