dcsimg

Installing Windows Installer Applications using WMI

By ServerWatch Staff (Send Email)
Posted Aug 29, 2001


by Marcin Policht

In the recent years, Microsoft has developed several new scripting technologies, intended to extend availability of the Windows Component Object Model beyond the reach of software developers. Most of them gained almost immediate popularity among system administrators, who finally had a set of tools much more flexible and powerful than batch files. In recent years, Microsoft has developed several new scripting technologies intended to extend availability of the Windows Component Object Model beyond the reach of software developers. In his latest article, Marcin Policht discusses one of these technologies, Windows Management Instrumentation (WMI), and specifically one of the management areas that can be controlled with WMI-based scripting, maintenance of the Windows Installer service.

However, one of these technologies, Windows Management Instrumentation, remained relatively unknown, mostly due to limited documentation. Microsoft's WMI Software Development Kit included a solid introduction to WMI, but it was targeted primarily at software programmers. As systems management became increasingly demanding and more complex, innovative scripting solutions utilizing WMI's powerful features started surfacing more frequently. This trend is bound to continue, judging from the enhanced functionality of WMI in Windows XP and .NET.

One of the management areas that can be controlled with WMI-based scripting is maintenance of the Windows Installer service. All of the required functionality is available through the Windows Installer provider. Providers are additional software components that "plug-in" into the WMI infrastructure in order to include in its management realm another software or hardware component. In this case, Windows Installer provider allows WMI to access the Windows Installer database.

In order to fully understand the way the provider interacts with the database, you need to be a bit familiar with the Windows Installer technology. Windows Installer looks at the installed software as hierarchical collection of components consisting of three tiers. The top tier is named product (for example Microsoft Office 2000 or Adobe Acrobat Reader 5.0) and it is represented internally by WMI as the Win32_Product class (WMI creates its own hierarchy of classes, called schema, which emulates structure of managed objects).

A product consists of one or more features, which has its WMI equivalent Win32_SoftwareFeature class. An example of such could be the Microsoft Word 2000 feature of the Microsoft Office 2000 product. Finally, each feature contains software elements, which the WMI schema mirrors using Win32_SoftwareElement class. The Spell Checker would qualify as a software feature of Microsoft Word. WMI maintains the relationship between instances of Win32_Product, Win32_SoftwareFeature, and Win32_SoftwareElement using association classes. More specifically: 

- Win32_ProductSoftwareFeatures represents relationship between Win32_Product and Win32_SoftwareFeature 

- Win32_SoftwareFeatureSoftwareElement represents relationship between Win32_SoftwareFeature and Win32_SoftwareElement

While a class is a definition of an object's characteristics, actual objects are represented by instances of this class. In more practical terms, in order to list all the Windows Installer applications installed on a target system, you need to enumerate (list) the instances of the Win32_Product class. This can be accomplished fairly easily, using the following script:

      strComputer =   "REPLACE_ME_!!!!" 'replace this name with the name of your target system
      strNamespace = "root/cimv2"
      strClass = "Win32_Product"

      '********************************
      '*** connect to root/cimv2 namespace on the target computer using moniker and
      '*** create an object representing collection of instances of Win32_Product class

      Set colInstances = GetObject("winmgmts:{impersonationLevel=impersonate}//" &
      _ strComputer & "/" & strNameSpace & ":" & strClass).Instances_

      '********************************
      '*** enumerate instances in the loop, for each, enumerate its properties
      '*** then within inner loop, for each property, list its name and value

      For Each objInstance In colInstances
          For Each objProperty In objInstance.Properties_
              WScript.Echo objProperty.Name & Chr(9) & objProperty.Value 
          Next
          WScript.Echo
      Next
      
Sample output would have the following format:

Caption Adobe Acrobat Reader
Description Adobe Acrobat Reader
IdentifyingNumber {04AC4A3A-4A5C-4319-9AFC-8C3AC9162E96}
InstallDate 20010630
InstallLocation is Null
InstallState 5
Name Adobe Acrobat Reader
PackageCache C:\WINNT\Installer\10bfc.msi
SKUNumber is Null
Vendor Adobe Systems, Inc.
Version 5.0


Similarly, if you wanted to find out all the features for all of the Windows Installer applications on the system, you would enumerate the instances of Win32_SoftwareFeature class. This would require only replacing the value of the strClass variable in the script above with the string "Win32_SoftwareFeatures". However, not only would this produce very long output, but also practical value of such output would be limited.

It is probably more desirable to list all of the features for particular product by using either IdentifyingNumber property (check the listing above), which is a GUID for that product, or using association between the Win32_Product and Win32_SoftwareFeature classes (a bit more tricky approach). Here is a sample script using the IdentifyingNumber property:

      strComputer = "REPLACE_ME_!!!!"   'replace this name with the name of your target system
      strProperty = "IdentifyingNumber"
      strValue = "{04AC4A3A-4A5C-4319-9AFC-8C3AC9162E96}"
      strNamespace = "root/cimv2"
      strClass = "Win32_SoftwareFeature"

      '********************************
      '*** form a WQL query used to search for all instances of Win32_SoftwareFeature class
      '*** with the IdentifyingNumber property equal to "{04AC4A3A-4A5C-4319-9AFC-8C3AC9162E96}"

      strWQLQuery = "SELECT * FROM " & strClass & " WHERE " & strProperty & "="
            & _ Chr(34) & strValue & Chr(34)

      '********************************
      '*** connect to root/cimv2 namespace on the target computer using moniker and
      '*** create an object representing collection of instances of Win32_SoftwareFeature
      '*** class, returned from execution of the WQL query

      Set colInstances = GetObject("winmgmts:{impersonationLevel=impersonate}//" & _
            strComputer & "/" & strNameSpace).ExecQuery(strWQLQuery, "WQL")

      '********************************
      '*** enumerate instances in the loop, for each, enumerate its properties
      '*** then within inner loop, for each property, list its name and value

      For Each objInstance In colInstances
          For Each objProperty In objInstance.Properties_
              WScript.Echo objProperty.Name & Chr(9) & objProperty.Value
          Next
          WScript.Echo
      Next
      
Functionality of WMI goes beyond simple enumeration. Win32_Product class has also a number of methods, which allow you to perform the following (both on local and remote systems) with any Windows Installer package: 

- Install (with any additional options, including transforms), reinstall, or uninstall 
- Perform administrative installation (to create administrative installation point), create advertisements, upgrade or configure existing installation.

For example, the following script installs a Windows Installer package on a remote system:

      strNameSpace = "root/cimv2"
      strClass = "Win32_Product"
      strComputer = "TARGET_COMPUTER"
                    'replace this name with the name of the target computer

      strDomain = "TARGET_DOMAIN"
                    'replace this name with the name of the Windows 2000
                    'domain where the target computer account resides

      strMethod = "Install"
      strPkgLoc = "\\PACKAGE_SERVER\PACKAGE_SHARE\PACKAGE_FOLDER\PACKAGE_NAME.MSI"
                    'replace this with the UNC path to the MSI file
                    'containing Windows Installer installation instructions

      strOptions = "TRANSFORMS=TRANSFORM_NAME.MST REBOOT=REALLYSUPRESS"
                    'specify installation options according to your needs

      strAllUsers = "True"

      '********************************
      '*** connect to root/cimv2 namespace on the target computer using
      '*** moniker and create an object representing a Win32_Product class
      '*** use delegate impersonation level and Kerberos authentication

      Set objInstance = GetObject("winmgmts:{impersonationLevel=delegate,authority=Kerberos:" & _
            strDomain & "\" & strComputer & "}//" & _
            strComputer & "/" & strNameSpace & ":" & strClass)

      '********************************
      '*** create an object representing "Install" method of Win32_Product class

      Set objMethod = objInstance.Methods_(strMethod)

      '********************************
      '*** set input parameters for the "Install" method

      Set objInParam = objMethod.inParameters.SpawnInstance_()
      objInParam.PackageLocation = strPkgLoc
      objInParam.Options = strOptions
      objInParam.AllUsers = strAllUsers

      '********************************
      '*** execute the method with the input parameters, and store
      '*** the returned object (indicating success or failure) in objOutParam

      Set objOutParam = objInstance.ExecMethod_(strMethod, objInParam)

      '********************************
      '*** display an appropriate message depending on the ReturnValue of objOutParam

      If objOutParam.ReturnValue = 0 Then
           WScript.Echo strMethod & " installation method completed successfully"
      Else
           WScript.Echo strMethod & " installation method failed"
      End If
	  
As long as source package installation files reside on a system different than the target computer and connection to them requires authentication, the script will have to use delegation mechanism (as indicated by the impersonationLevel moniker parameter). Delegation is not possible with NTLM but instead requires Kerberos authentication, available only in a Windows 2000 Active Directory environment. In addition, the account of a user running the script needs to be trusted for delegation (you can configure this setting by modifying user account properties with Active Directory Users and Computers or programmatically via ADSI). The same applies to the account of the target computer (part of computer account properties accessible also with Active Directory Users and Computers or ADSI).

There are, however, workarounds, which allow remote installation in Windows NT domains. Both use impersonation (which is available, and set as default in a Windows NT domain) instead of delegation.

First one, not realistic with large deployments, but possible with individual installations, involves copying a package to a target computer and using local path when specifying location of source files. This eliminates the need for authenticated network operation to access a package source server.

The second one, much more practical, although raising some security concerns, is based on MS Knowledge Base article Q289655. In short, level of authentication required to access a network share can be lowered by modifying REG_MULTI_SZ value of NullSessionShares registry entry at the location: HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters on a server storing the Windows Installer packages. The entry contains list of share names accessible to Everyone, including a local system account. In addition, you should add Everyone group to Pre-Windows 2000 Compatible Access group, if you want to use remote installation on Windows NT 4.0 clients.

In case you decide to use these workarounds, you will need to replace the line:

Set objInstance = _
      GetObject("winmgmts:{impersonationLevel=delegate,authority=Kerberos:" & _
      strDomain & "\" & strComputer & "}//" & _
      strComputer & "/" & strNameSpace & ":" & strClass)
      
in the previous script with the following one:
Set objInstance = _
      GetObject("winmgmts:{impersonationLevel=impersonate}//" & _
      strComputer & "/" & strNameSpace & ":" & strClass)
      

Page 1 of 1


Comment and Contribute

Your name/nickname

Your email

(Maximum characters: 1200). You have characters left.