# encoding: ascii
# api: powershell
# title: CIM SMI-S Query Library
# description: The function queries any CIM server, called CIMOM, that supports the CIM-XML interface. It creates an XML message to encapsulate the CIM query, converts the message to byte stream and then sends it using HTTP POST method. The response byte stream is converted back to XML message and name value paris are parsed out. SMI-S is an instance of CIM, and is thus also fully supported.
# version: 1.0
# type: function
# author: Parul Jain
# license: CC0
# function: cim-ei
# x-poshcode-id: 3355
# x-archived: 2014-04-08T03:53:58
# x-published: 2014-04-14T05:29:00
#
#
function cim-ei {
<#
.SYNOPSIS
Enumerate Instances of a class on a CIMOM via CIM-XML interface
.DESCRIPTION
Primary use case of this function is to gather inventory and performance information from IT
infrastructure assets. The inventory information feeds into capacity planning, troubleshooting,
managing product life cycle, budgeting, vendor price negotiations and technology strategy in
large enterprise environments. The output from this function would typically go into a datawarehouse
front ended with a business intelligence platform such as COGNOS, QlikView, Business Objects, etc.
The function queries any CIM server, called CIMOM, that supports the CIM-XML interface. It
creates an XML message to encapsulate the CIM query, converts the message to byte stream and
then sends it using HTTP POST method. The response byte stream is converted back to XML message
and name value paris are parsed out. SMI-S is an instance of CIM, and is thus also fully supported.
Tested against SAN devices such as EMC Symmetrix VMAX Fibre Channel Storage Array and Cisco MDS
Fibre Channel switch. It can be used to query VMWARE vSphere vCenter, IBM XIV, NetApp Filer, EMC
VNX Storage Array, HP Insight Manager, Dell OpenManage, HDS: USP, USPV, VSP, AMS, etc.
.NOTES
Author: Parul Jain (paruljain@hotmail.com)
Version: 0.1, 14th April, 2012
Requires: PowerShell v2 or better
.EXAMPLE
cim-ei -Class CIM_ComputerSystem -Device switch1 -user admin -Pass securepass
.EXAMPLE
This works with EMC Symmetrix
cim-ei -Class CIM_ComputerSystem -Device seserver -user admin -Pass '#1Password' -ns 'root/emc'
.PARAMETER class
Mandatory. Information within CIM is classified into classes. The device documentation (or SNIA
documntation in case of SMI-S) should list all the classes supported by the CIMOM. CIM_ComputerSystem
class is available universally and is a good place to start testing.
.PARAMETER device
Mandatory. IP address or DNS name of the device or CIMOM server if CIMOM runs separately
.PARAMETER user
Mandatory. User ID authorized to perform queries. Most hardware vendors have a factory default
.PARAMETER pass
Mandatory. Password for the user. Again most hardware vendors have a factory default for servicing the equipment
.PARAMETER port
Optional. The TCP port number that the CIMOM is listening to. Default is used if not specified.
.PARAMETER ssl
Optional switch. When used function will use HTTPS instead of default HTTP
.PARAMETER ns
Optional. CIM namespace to use. Default is root/cimv2. EMC uses root/emc
.PARAMETER msg
Optional switch. Returns CIM-XML response message instead of parsed name-value pairs for
troubleshooting parsing if needed
.PARAMETER localOnly
Optional switch. LocalOnly and Deep (Inheritance) switches work together to define precisely the properties
that are to be returned. Properties from the specified class are always returned but properties from
subclasses and superclasses of the specified class can be included or excluded as required.
.PARAMETER deep
Optional switch. LocalOnly and Deep (Inheritance) switches work together to define precisely the properties
that are to be returned. Properties from the specified class are always returned but properties from
subclasses and superclasses of the specified class can be included or excluded as required.
.PARAMETER classOrigin
Optional switch. Specifies whether the name of the class in which the property or method was defined
(possibly a superclass of this one) should be included in the response.
.PARAMETER qualifiers
Optional switch. Specifies whether or not qualifiers for each instance and property are to be returned.
#>
[CmdletBinding()]
Param (
[parameter(Mandatory=$true, ValueFromPipeline=$true)][String]$Class,
[parameter(Mandatory=$true, ValueFromPipeline=$false)][String]$User,
[parameter(Mandatory=$true, ValueFromPipeline=$false)][String]$Pass,
[parameter(Mandatory=$true, ValueFromPipeline=$false)][String]$Device,
[parameter(Mandatory=$false, ValueFromPipeline=$false)][String]$Port = '',
[parameter(Mandatory=$false, ValueFromPipeline=$false)][Switch]$ssl,
[parameter(Mandatory=$false, ValueFromPipeline=$false)][String]$ns = 'root/cimv2',
[parameter(Mandatory=$false, ValueFromPipeline=$false)][Switch]$localOnly,
[parameter(Mandatory=$false, ValueFromPipeline=$false)][Switch]$classOrigin,
[parameter(Mandatory=$false, ValueFromPipeline=$false)][Switch]$deep,
[parameter(Mandatory=$false, ValueFromPipeline=$false)][Switch]$qualifiers,
[parameter(Mandatory=$false, ValueFromPipeline=$false)][Switch]$msg
)
# CIM-XML message template
$messageText = @'
<?xml version="1.0" encoding="utf-8" ?>
<CIM CIMVERSION="2.0" DTDVERSION="2.0">
<MESSAGE ID="1000" PROTOCOLVERSION="1.0">
<SIMPLEREQ>
<IMETHODCALL NAME="EnumerateInstances">
</IMETHODCALL>
</SIMPLEREQ>
</MESSAGE>
</CIM>
'@
# Parse the XML text into XMLDocument
$message = [xml]($messageText)
# Transform CIM-XML message based on supplied parameters
$nsPathNode = $message.cim.message.SIMPLEREQ.IMETHODCALL.AppendChild($message.CreateElement('LOCALNAMESPACEPATH'))
foreach ($path in $ns.split('/')) {
$pathElement = $nsPathNode.AppendChild($message.CreateElement('NAMESPACE'))
$pathElement.SetAttribute('NAME', $path)
}
$paramNode = $message.CIM.MESSAGE.SIMPLEREQ.IMETHODCALL
$param = $paramNode.AppendChild($message.CreateElement('IPARAMVALUE'))
$param.SetAttribute('NAME', 'ClassName')
$paramValue = $param.AppendChild($message.CreateElement('CLASSNAME'))
$paramValue.SetAttribute('NAME', $class)
$param = $paramNode.AppendChild($message.CreateElement('IPARAMVALUE'))
$param.SetAttribute('NAME', 'LocalOnly')
$paramValue = $param.AppendChild($message.CreateElement('VALUE'))
if ($localOnly) { $paramValue.InnerText = 'TRUE' } else { $paramValue.InnerText = 'FALSE' }
$param = $paramNode.AppendChild($message.CreateElement('IPARAMVALUE'))
$param.SetAttribute('NAME', 'IncludeClassOrigin')
$paramValue = $param.AppendChild($message.CreateElement('VALUE'))
if ($classOrigin) { $paramValue.InnerText = 'TRUE' } else { $paramValue.InnerText = 'FALSE' }
$param = $paramNode.AppendChild($message.CreateElement('IPARAMVALUE'))
$param.SetAttribute('NAME', 'DeepInheritance')
$paramValue = $param.AppendChild($message.CreateElement('VALUE'))
if ($deep) { $paramValue.InnerText = 'TRUE' } else { $paramValue.InnerText = 'FALSE' }
$param = $paramNode.AppendChild($message.CreateElement('IPARAMVALUE'))
$param.SetAttribute('NAME', 'IncludeQualifiers')
$paramValue = $param.AppendChild($message.CreateElement('VALUE'))
if ($qualifiers) { $paramValue.InnerText = 'TRUE' } else { $paramValue.InnerText = 'FALSE' }
# Do not validate server certificate when using HTTPS
# Amazing how easy it is to create a delegated function in PowerShell!
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
# Process other parameters and switches
$protocol = 'http://'
if ($ssl) { $protocol = 'https://' }
if ($port -eq '' -and !$ssl) { $port = '5988' }
if ($port -eq '' -and $ssl) { $port = '5989' }
$url = $protocol + $device + ":" + $port
# Instantiate .Net WebRequest class
$req = [System.Net.WebRequest]::Create($url + '/cimom')
$req.Method ='POST'
# Add headers required by CIMOM
$req.ContentType = 'application/xml;charset="UTF-8"'
$req.Headers.Add("CIMProtocolVersion", "1.0")
$req.Headers.Add('CIMOperation', 'MethodCall')
$req.Headers.Add('CIMMethod', 'EnumerateInstances')
$req.Headers.Add('CIMObject', $ns)
# Encode and attach userID and password
$uri = New-Object System.Uri($url)
$nc = New-Object System.Net.NetworkCredential($user, $pass)
$cc = New-Object System.Net.CredentialCache
$cc.add($uri, 'Basic', $nc)
$req.Credentials = $cc
$enc = New-Object System.Text.UTF8Encoding
$bytes = $enc.GetBytes($message.OuterXML)
$req.ContentLength = $bytes.Length
$reqStream = $req.GetRequestStream()
$reqStream.Write($bytes, 0, $bytes.Length)
$reqStream.Close()
# Send the request
try {
$resp = $req.GetResponse()
} catch [Net.WebException] { throw($_.Exception.Message) }
# Parse the response XML
$reader = new-object System.IO.StreamReader($resp.GetResponseStream())
$result = [xml]($reader.ReadToEnd())
$reader.Close()
# Create a temporary XML document to help parse out name value pairs
# There are several other ways this can be accomplished
$xdoc = new-object xml
$rootnode = $xdoc.AppendChild($xdoc.CreateElement($class))
foreach ($instance in @($result.CIM.MESSAGE.SIMPLERSP.IMETHODRESPONSE.IRETURNVALUE.'Value.NamedInstance')) {
$node = $rootnode.AppendChild($xDoc.CreateElement('Property'))
foreach ($prop in @($instance.instance.property)) {
if ($prop.value -ne $null -and $prop.value -ne '') {
$node.SetAttribute($prop.Name, $prop.value)
}
}
}
# Return CIM-XML response message or parsed out array of name value pairs
if ($msg) { $result } else { $xdoc.$class.property }
}