Windows PowerShell 5.0 is a dynamic and object-oriented scripting language. Compared to any other scripting language, it provides more benefits, such as reliability, security, managed code environments, and so on. Windows PowerShell has a strong connection with Windows Management Instrumentation (WMI), Common Information Model (CIM), Extensible Markup Language (XML), and so on. Using this, we can develop solutions to automate our tasks.
PowerShell has the ability to manage different technologies. Using the PowerShell API, we can manage custom-built applications as well.
In this section, we will cover the following topics:
WMI—Windows Management Instrumentation—is the Microsoft implementation of WBEM—Web Based Enterprise Management—which allows us to access management information from any environment. PowerShell makes access to WMI easy and consistently deliverable using an object-based technique.
Let's explore a few PowerShell cmdlets of WMI.
WMI is a part of the Microsoft.PowerShell.Management
module. You can run the following command to explore the WMI cmdlets:
Get-Command -Module Microsoft.PowerShell.Management -Name '*WMI*'
The output is illustrated in the following image:
There are many tools available to explore all the WMI classes available in the WMI repository. You can use the following links to do so:
https://www.sapien.com/software/wmiexplorer
Using these tools, we can explore and view the classes, instances, properties, and qualifiers easily. It's a GUI tool, so it makes our job easy as well. As we are focusing more on PowerShell, let's do it the PowerShell way, on the fly and explore.
The Get-WmiObject
cmdlet has a switch parameter to list all the classes. The Get-WmiObject –List
command will retrieve all the classes from the RootCIMV2
namespace by default. However, we can explicitly mention the namespace using the NameSpace
parameter to identify the specified WMI class location, as shown in the following command:
#Retrieves WMI Class from RootSecurity NameSpace Get-WmiObject -List -Namespace 'RootSecurity' #Retrieves WMI Class from RootCIMV2 - Default Get-WmiObject -List
To know more about WMI cmdlets, use the help about_WMI_Cmdlets
command.
PowerShell supports the WMIClass
type accelerators, which is a shortcut for using .NET classes in PowerShell.
These make PowerShell richer and more useful; therefore, system administrators can use WMI in PowerShell very easily.
Let's query a service named WinRM
using the WMI type accelerator. Run the following command:
[wmi]"rootcimv2:Win32_Service.Name='WinRM'"
The same can be achieved using the following Windows PowerShell commands:
Get-WmiObject -Class Win32_Service -Filter "Name='WinRM'" Get-WmiObject -Class Win32_Service | ? {$_.Name -eq 'WinRM'} (Get-WmiObject -Class Win32_Service).Where({$_.Name -eq 'WinRM'})
All of the preceding commands provide the same result, and it depends upon the usage and optimization. The Get-WMIObject
cmdlet has a parameter named Query
, which allows us to use
WMI Query Language (WQL), as in the following command:
Get-WmiObject -Query "Select * from Win32_Service where Name='WinRM'"
Using the WMIClass
type accelerator, we can invoke any method easily, as shown in the following command:
$Obj = [wmiclass]"Win32_Process" $Obj.Create('NotePad.exe')
The output of this command is as shown in the following image:
The WMI provider consists of the Managed Object Format (MOF) file, which defines the data, classes, and associated events. Using the WMIClass
type accelerator method, it's possible to explore the MOF file. Consider the following commands:
$Obj = [wmiclass]"Win32_OperatingSystem" $Obj.GetText('MOF')
The preceding commands would output the MOF file as shown in the following image—you can use the Out-GridView
cmdlet for look and feel:
The same MOF file can be found at $ENV:WindirSYSTEM32WBEM
. Use the following command:
Get-ChildItem C:windowsSystem32wbem -Filter *.MOF
Windows PowerShell has a command named Invoke-WMIMethod
, which is used to invoke methods without using type accelerators.
Note that in the following code, we have used | Out-Null
, which deletes the output instead of sending it to the pipeline:
Invoke-WmiMethod -Class Win32_process -Name Create -ArgumentList Notepad.exe | Out-Null
This is similar to casting to Void
type, as shown in the following command:
[Void](Invoke-WmiMethod -Class Win32_process -Name Create -ArgumentList Notepad.exe)
Using the WMISearcher
type accelerator, we can explore the WMI data. Now, let's try the following code:
$Obj = [wmisearcher]"Select * from Win32_Process Where Name = 'Notepad.exe'" $Obj
Let's consider what this is. This outputs Scope
, Query
, Options
, Site
, and Container
, which are the properties. To get the result collections we need to invoke GetMethod()
method.
$Obj = [wmisearcher]"Select * from Win32_Process Where Name = 'Notepad.exe'" $Obj.Get()
Using pipelines, we can select the properties we need to view, as in the following command:
$Obj = [wmisearcher]"Select * from Win32_Process Where Name = 'Notepad.exe'" $Obj.Get() | Select Caption , ExecutablePath , UserModeTime
The output is illustrated in the following image:
The points marked in the figure are explained in the following list:
Caption
, ExecutablePath
, and UserModeTime
Let's consider a demo of a tiny PowerShell function using WMI.
We will need to retrieve the BIOS, computer system, and operating system information from the given servers in the environment.
We will use the WIN32_BIOS
, Win32_ComputerSystem
, and Win32_OperatingSystem
classes to get the information.
Execute the following code:
Function Get-SystemInformation { param( [parameter(Mandatory = $true)] [String]$computername ) $OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername $BIOS = Get-WmiObject -Class Win32_BIOS -ComputerName $computername $CS = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername $properties = New-Object psobject -Property @{ "OSName" = $os.Caption "ServicePack" = $os.CSDVersion "SerialNumber" = $BIOS.SerialNumber "Manufacturer" = $BIOS.Manufacturer "Bootupstate" = $cs.BootupState } $properties } Get-SystemInformation -computername localhost
Let's consider how this works. Perform the following steps:
Function
keyword to create a function.Get-SystemInformation
as this is a friendly name, but ensure that you follow the verb-noun combination for easy understanding.Param
block to declare a variable.$ComputerName
(For now, we will use a localhost).Win32_BIOS
, Win32_ComputerSystem
, and Win32_OperatingSystem
and assign each a variable.PSObject
class and collect all information in $Properties
object.The output is illustrated in the following image:
WMI uses Distributed COM (DCOM) to connect to a remote computer. However, in certain environments, this may be blocked by a firewall. In this scenario, we can retrieve the information using the PowerShell remoting feature or using the WSMan object.
To explore the WSMan commands, you can execute the following command:
Get-Command -Module Microsoft.WSMan.Management
The output is illustrated in the following image:
As we are discussing the basics of WMI here, we are not covering all the topics. However, we will discuss more about WMI and CIM in further topics.
CIM is defined by Distributed Management Task Force (DMTF) and is an object-oriented data model. In WMI, developers can use CIM to create classes. Using CIM, it's easy to manage the different elements of an environment.
CIM cmdlets are introduced in PowerShell 3.0, and these are vendor independent. Considering the recent Cloud operating system, we will have to work with different manufacturers. So, using CIM is the best option because WMI is Windows-based; it implements the DMTF standards in CIM and also allows us to query non-Windows operating systems.
The advantages of CIM in PowerShell are its usability and ability to run quite faster than WMI. PowerShell facilities such as tab completion make CIM cmdlets very rich. In other words, CIM is a superset of WMI.
To know the available CIM cmdlets, we can simply run the following code:
(Get-Command -Noun CIM*).Name
It will return the following output:
Get-CimAssociatedInstance Get-CimClass Get-CimInstance Get-CimSession Invoke-CimMethod New-CimInstance New-CimSession New-CimSessionOption Register-CimIndicationEvent Remove-CimInstance Remove-CimSession Set-CimInstance
Let's query the basic OS and BIOS information as follows:
Function Get-SystemInformation { param( [Parameter(Mandatory = $true,ValueFromPipeline = $true)] [string[]]$ComputerName ) Begin{} Process{ foreach($computer in $ComputerName) { $OS = Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $computer $BIOS = Get-CimInstance -ClassName Win32_BIOS -ComputerName $computer $props = New-Object psobject -Property @{ OSName = $os.Caption ServicePack = $OS.CSDVersion BIOSSerialNumber = $BIOS.SerialNumber BIOSReleaseDate = $BIOS.ReleaseDate } } $props } End{} } #Demo "localhost" ,"Localhost" | %{ Get-SystemInformation -ComputerName $_ }
Note that every time you run the function, the output order is different. To get an ordered output, we can make a minor change in the code, as shown in the following:
$props = [Ordered] @{ OSName = $os.Caption ServicePack = $OS.CSDVersion BIOSSerialNumber = $BIOS.SerialNumber BIOSReleaseDate = $BIOS.ReleaseDate } New-Object psobject -Property $props
The preceding code returns an ordered output, as shown in the following image:
The points marked in the figure are explained in the following list:
OSName
lists the names of operating systemsServicePack
gives service pack informationBIOSSerialNumber
gives BIOS serial number informationBIOSReleaseDate
lists the release date of operating systemsWhat does [Ordered]
do here? It simply makes an ordered dictionary. Let's take a look at another example of this.
The following code creates a hash table; the output order will be random:
$props = @{A='1' B='2' C='3' }
To identify the type name, we use the GetType()
method, which returns a hash table as names. Following is the command:
$props.GetType()
The following code will return an output in the same order (A, B, and C). This is an ordered dictionary type:
$props = [Ordered]@{A='1' B='2' C='3' }
CIM cmdlets are introduced in PowerShell 3.0; so, before using CIM to query devices, we should ensure that it complies with the CIM and
WSMan standards defined by DMTF. You may wonder, how can we use CIM while querying in a mixed environment, where we may have Windows Server 2012 and 2008 R2 with PowerShell 2.0? The CIM class works on the devices that have PowerShell version 3.0. What happens if we try to use the Invoke-Command
cmdlet? It will fail with an error message, 'Get-CimInstance' is not recognized as the name of a cmdlet, function, script file, or operable program
.
The solution is to use a CIM session. Let's take a look at how to use the CIM session in this example. Execute the following code:
Test-WSMan -ComputerName RemoteServer
The output is illustrated in the following image—the version used here is 2.0:
All we need to know in the two CIM cmdlets are their parameters, which are as follows:
New-CimSessionOption
cmdletGet-CimInstance
cmdletExecute the following code:
$dcom = New-CimSessionOption -Protocol Dcom $Remote = New-CimSession -ComputerName 'RemoteServer' -SessionOption $dcom (Get-CimInstance -CimSession $Remote -ClassName CIM_OperatingSystem).InstallDate
Let's take a look at how this works.
We used the DCOM protocol, which is New-CimSessionOption
, and assigned it to a $dcom
variable. Then, we used the New-CIMSession
cmdlet to create a session with the -SessionOption
parameter (we used $dcom
, which is nothing but a DComSessionOptions
type). Finally, we used the Get-CimInstance
cmdlet and consumed $Remote
, which is the CimSession
type, with the DCOM protocol.
To remove the CIM session, we will simply use the following snippet:
Get-CimSession | Remove-CimSession
The benefits of using CIM cmdlets are as follows:
Get-CimClass
to explore a WMI class