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:WINNTInstaller10bfc.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_SERVERPACKAGE_SHAREPACKAGE_FOLDERPACKAGE_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:
HKLMSystemCurrentControlSetServicesLanmanServerParameters 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)