We have a guest blogger today.  I would like to thank Curtis Sawin for writing this great article on how to automate creating and publishing updates via PowerShell.

In previous posts, we demonstrated how one can create and publish a Windows Installer Patch (MSP file) to WSUS for use by Configuration Manager (or SC Essentials). The next step is to automate this process.

In order to do this, one must use the WSUS APIs. There is no COM automation interface available, which excludes VBScript. PowerShell is excellent for leveraging such APIs. Below is some sample code that demonstrates how to leverage PowerShell to script the WSUS APIs. The full script can be downloaded from the TechNet Script Center Repository.

Notes

The code below is designed for simplicity and readability. Meaning, there’s no error handling, and hard-coded paths are used. It’s designed for demo purposes only. Another important item to note is that SCUP is not used at all in this process. Since SCUP is built entirely on the WSUS APIs, the below code leverages the WSUS APIs as well. This means two things:

· One does not need to install SCUP to run the below code. One needs to install either the WSUS admin console or run the code on a WSUS server to have the APIs available.

· Any update that is created and published using the WSUS APIs directly will not appear in the SCUP console.

When publishing custom updates to Configuration Manager, one must configure the Software Update Point component (see screen shot below) to sync your custom updates. Specifically, the vendor and product must be selected in the SUP component properties (Site Management >> [Site Name] >>Site Settings >> Component Configuration).

clip_image002

Overview

The basic process is as follows:

1. Define the MSP file to be published

2. Connect to the WSUS namespace

3. Create the catalog xml file in memory

4. Add the appropriate metadata (title, description, etc)

5. Save the catalog file to the file system

6. Publish the catalog and MSP file to WSUS

Details

Step 1 – Define the MSP file to be published

# Define MSP file

$pkgPath = "C:\demo1\sample1\sample1.msp"

Step 2 – Connect to the WSUS Namespace

Load the WSUS namespace and create a SoftwareDistributionPackage object.

# Load the WSUS Namespace and create SoftwareDistributionPackage object

[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null

$sdp = new-object('microsoft.updateservices.administration.SoftwareDistributionPackage')

Step 3 – Create the catalog XML file in memory

$sdp.PopulatePackageFromWindowsInstallerPatch($pkgPath)

Create the basic catalog xml file by calling PopulatePackageFromWindowsInstallerPatch. This method does 90% of the work in this script. It generates the catalog xml file for you. At this point the XML file is in memory only.

Step 4 – Add the appropriate metadata (title, description, etc)

Next, one must create the required properties in order to publish to WSUS.

# Add properties

$sdp.Title = "Sample1"

$sdp.Description = "This is the 1st sample MSP update"

$sdp.VendorName = "MyCompany"

$sdp.PackageType = "1" # Update

$sdp.PackageUpdateClassification = "3" # Security Updates

$sdp.SecurityRating = "1" # Critical

$sdp.ProductNames.Add("MyProduct") | out-null

$sdp.AdditionalInformationUrls.Add("http://bing.com")

Couple notes here. First, if one were to use SCUP to create an update, the properties listed above are all highlighted as required in order to proceed through the wizard. Second, the PackageType, PackageUpdateClassification, and SecurityRating are enumerations, and the above code sets their integer values. Lastly, note the ProductNames and AdditionalInformationUrls properties. These properties are StringCollections. In order to create a product name or a single URL, one must add an item to the collection by using the Add method.

Next is the (relatively) tricky part. Each SoftwareDistributionPackage object contains at least one InstallableItem. Further, an InstallableItem element contains information about the update file, including the file name, file size, timestamp, URL location, and the file digest. This information is stored in the OriginalSourceFile property of the InstallableItem; and this property represents a FileForInstallableItem object (whew!). This means that one must create a new FileForInstallableItem object, set its properties, then set the OriginalSourceFile property with this object.

So what’s a file digest? Basically, it’s the hash value of the update file, represented in a specific format (SHA-1 using base 64 encoding). In order to get the hash value one must use the ComputeHash method of the SHA1CryptoServiceProvider class.

# Get hash value of MSP file

$pkg = (new-object("System.IO.FileInfo")($pkgPath))

$provider = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider

$digest64 = [System.Convert]::ToBase64String($provider.ComputeHash($pkg.OpenRead()))

$instream.Close | out-null

# Define file properties of MSP

$FileItem = new-object('microsoft.updateservices.administration.FileForInstallableItem')

$FileItem.FileName = "sample1.msp"

$FileItem.OriginUri = "file://mycomputer/demo1/sample1/sample1.msp"

$FileItem.Digest = $digest64

$FileItem.Modified = $pkg.LastWriteTimeUtc

$FileItem.Size = $pkg.Length

$sdp.InstallableItems[0].OriginalSourceFile = $FileItem

Step 5 – Save the catalog file to the file system

At this point, the catalog XML file is fully populated. However, before one can publish it, the catalog file must be saved to the file system.

# create update catalog file

$sdpFilePath = "C:\demo1\sample1.xml"

$sdp.Save($sdpFilePath)

Step 6 – Publish the catalog and MSP files to WSUS

The only task left is to publish the update to WSUS. This can be done by connecting to the WSUS server, then publishing the update by using the PublishPackage method. The GetUpdateServer method is used to connect to the WSUS server, and the GetPublisher method creates an IPublisher instance that represents the update to be published. If the WSUS server is local, one does not need to provide any parameters to the GetUpdateServer method. Otherwise, one must provide the remote WSUS server.

# Get WSUS server and publish

$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer();

$publisher = $wsus.GetPublisher($sdpFilePath)

$dir = $pkg.Directory

$publisher.PublishPackage($dir, $null)

Note that the PublishPackage method takes a directory as a parameter and it places everything in that directory into a cab file. So make sure that only relevant files (like your MSP file) are included in the directory. Anything else in this directory will get published to the WSUS server.

After your update is published to WSUS, it will appear in the Configuration Manager (or SC Essentials) console after the next update synchronization.

Summary

Publishing MSP files as custom updates is easy to do. It’s much easier than creating and publishing an update out of an MSI or an EXE, as one does not need to create the applicability rules. When the PopulatePackageFromWindowsInstallerPatch method is generated, the catalog file automatically gets updated with all MSI ProductCodes to which the MSP applies.

This automation can be especially helpful to IT organizations that would like to deploy 3rd party MSP files as updates, as it provides the ability to “one-click” the publishing of an MSP file to Configuration Manager. It’s even more beneficial to IT organizations that create their own MSP files that may do this task frequently.

Again, the full script can be downloaded from the TechNet Script Center Repository. Additionally, below are some links that also provide sample code:

Script Center - Publish an MSP Package into a WSUS Server

MSDN Library – Local Publishing of Updates and Applications