In this chapter, you will learn to:
Here we will show you how to use PowerShell and PowerCLI to create reports, where to get the data for your reports, and how to create reports in all kinds of formats. When you finish, be sure to take a look at Appendix A for example report scripts that you can use as a basis for your own automated reports.
To get you started, let’s begin with some basic concepts. Besides a brief introduction to the PowerCLI Get
cmdlets, which allow you to retrieve information, this section introduces some techniques for ordering and grouping the data.
Data in PowerShell is presented through objects. You will learn how you can create your own objects, and how to extend the objects returned by cmdlets.
The basic building block that you will use for creating reports are the numerous Get
cmdlets that are available in PowerCLI. A Get
cmdlet usually returns one or more objects; each holds a number of properties that you can select for your report. Consider the next example:
Get-VM
Name PowerState Num CPUs MemoryGB
---- ---------- -------- --------
VM3 PoweredOff 1 2.000
VM2 PoweredOff 1 2.000
VM1 PoweredOff 1 4.000
The Get-VM
cmdlet in this example returned three objects that each represent a virtual machine (VM). Remember that the PowerCLI cmdlets display only a limited set of the available properties for the objects.
With the Get-Member
cmdlet you can see all the available properties on an object. For example:
Get-VM | Get-Member -MemberType Property | Select-Object -Property Name
Name
----
CDDrives
Client
CustomFields
DatastoreIdList
Description
DrsAutomationLevel
ExtensionData
FloppyDrives
Folder
FolderId
Guest
GuestId
HAIsolationResponse
HardDisks
...
Note that the last example only shows some of the available properties; there are many more.
Once you have determined which properties you want to include in your report, start composing your Select-Object
line:
Get-VM | Select-Object -Property Name,PowerState,GuestId,VMHost |
Format-Table -AutoSize
Name PowerState GuestId VMHost
---- ---------- ------- ------
VM3 PoweredOff windows8_64Guest esx61.local.test
VM2 PoweredOff windows8_64Guest esx61.local.test
VM1 PoweredOff windows9Server64Guest esx62.local.test
In the example, we piped the result of the Select-Object
cmdlet to the Format-Table
cmdlet with the AutoSize
switch. This makes the output more compact.
Although you can create great reports with the available properties on the objects returned by all the PowerCLI Get
cmdlets, there are occasions when you want to display derived or more complex information in your report. That is where the calculated property is your friend. A calculated property is in fact a hash table with two elements: a name and an expression. In the expression element, you can provide a value for the property that you define in the name element.
The VirtualMachine
objects that are by default returned by the Get-VM
cmdlet include a property called ProvisionedSpaceGB
, but since that property is defined as a decimal, it is displayed with a long series of values to the right of the decimal:
Get-VM | Select-Object -Property Name,PowerState,ProvisionedSpaceGB |
Format-Table -AutoSize
Name PowerState ProvisionedSpaceGB
---- ---------- ------------------
VM3 PoweredOff 34.140256862156093120574951172
VM2 PoweredOff 34.140256862156093120574951172
VM1 PoweredOff 44.140256862156093120574951172
In the next example, we use a .NET method from the System.Math
class to round the decimal and display the data in a much more user-friendly fashion.
Get-VM | Select-Object Name,PowerState,
@{N='ProvSpaceGB';E={
[Math]::Round($_.ProvisionedSpaceGB,1)
}} |
Format-Table -AutoSize
Name PowerState ProvSpaceGB
---- ---------- -----------
VM3 PoweredOff 34.1
VM2 PoweredOff 34.1
VM1 PoweredOff 44.1
In the next example, we used calculated properties to demonstrate how you can use calculated properties not only for formatting the result but also to create new properties:
Get-VMHost |
Select @{N='Name';E={$_.Name.Split('.')[0]}},
@{N='MemUsage%';E={
"{0:P1}" -f ($_.MemoryUsageMB/$_.MemoryTotalMB)
}}
Name MemUsage%
---- ---------
esx1 18.1 %
esx2 34.0 %
In the previous example, we used the full names for all parts in the PowerShell code. But you can abbreviate several; PowerShell accepts a lot of abbreviations. Since PowerShell is intended for administrators, it foresees that typing lots of text is not your favorite pastime. You can use aliases (Select
instead of Select-Object
), abbreviated hash table element names (N
instead of Name
), and short notations for .NET classes ([math]
instead of [System.Math]
).
In the previous section, we showed a very high-level view of how to create reports with the help of PowerCLI. In this section, we are going to give more techniques that are useful when creating reports.
PowerShell has some basic cmdlets that allow you to manipulate the data. One of these is the Sort-Object
cmdlet. In the previous section, the returned objects were ordered as they were provided by PowerCLI. To make your reports more user friendly, you can impose a specific order on the returned objects.
Get-VM | Select Name,PowerState,
@{N='ProvSpaceGB';E={
[Math]::Round($_.ProvisionedSpaceGB,1)
}} |
Sort-Object -Property Name |
Format-Table -AutoSize
Name PowerState ProvSpaceGB
---- ---------- -----------
VM1 PoweredOff 44.1
VM2 PoweredOff 44.1
VM3 PoweredOff 17.1
VM4 PoweredOff 34.1
By simply piping the objects through the Sort-Object
cmdlet, you can define the order of returned objects. The Sort-Object
cmdlet allows the order to be imposed on any property and any sequence you like.
Get-VM | Select Name,PowerState,
@{N='ProvSpaceGB';E={
[Math]::Round($_.ProvisionedSpaceGB,1)
}} |
Sort-Object -Property ProvSpaceGB -Descending |
Format-Table -AutoSize
Name PowerState ProvSpaceGB
---- ---------- ------------------
VM2 PoweredOff 44.1
VM1 PoweredOff 44.1
VM4 PoweredOff 34.1
VM3 PoweredOff 17.1
You can also use multiple properties in your sort. If you want to have a different order for each of the properties, you will have to use a hash table.
Get-VM | Select Name,PowerState,
@{N='ProvSpaceGB';E={
[Math]::Round($_.ProvisionedSpaceGB,1)
}} |
Sort-Object -Property @{Expression='ProvSpaceGB'; Descending=$True},
@{Expression='Name'; Descending=$false} |
Format-Table -AutoSize
Name PowerState ProvSpaceGB
---- ---------- -----------
VM1 PoweredOff 44.1
VM2 PoweredOff 44.1
VM4 PoweredOff 34.1
VM3 PoweredOff 17.1
Be careful when you are sorting on enumeration values. The string you see is not necessarily the internal integer value—the sort order returned could turn out to be something different than you expected.
Get-Cluster -Name cluster1 |
Get-DrsRule |
Sort-Object Type |
Select Name,Type |
Format-Table -AutoSize
Name Type
---- ----
Anti-Affinity VMAntiAffinity
Affinity VMAffinity
In this example, we wanted to sort on the DRS rule type, but alphabetically VMAntiAffinity
does not come before VMAffinity
. This occurs because the Type
property is an enumeration type. Behind the user-friendly text, there are integer values, and it is those integer values the Sort-Object
cmdlet uses. In the next script, we list the text and the value behind an enumeration, so you can more clearly see what Sort-Object
does.
$rules = Get-Cluster -Name cluster1 | Get-DrsRule
$type = $rules[0].Type
[Enum]::GetValues($type.GetType()) |
Select @{N="UserFriendlyName";E={$_.ToString()}},
@{N='InternalValue';E={$_.Value__}}
UserFriendlyName InternalValue
---------------- -------------
VMAntiAffinity 0
VMAffinity 1
VMHostAffinity 2
[Enum]::GetValues($type.GetType()) |
Select @{N="UserFriendlyName";E={$_.ToString()}}, value__
UserFriendlyName value__
---------------- -------
VMAntiAffinity 0
VMAffinity 1
VMHostAffinity 2
But how can you sort in the alphabetical order? It turns out that alphabetical sorting is quite easy when you cast the Type
property to a string on the Property
parameter of the Sort-Object
cmdlet:
Get-Cluster -Name cluster1 |
Get-DrsRule |
Sort-Object -Property {[string]$_.Type} |
Select Name,Type |
Format-Table -AutoSize
Name Type
---- ----
Affinity VMAffinity
Anti-Affinity VMAntiAffinity
To produce a report that groups specific entities together based on a property, you can use the Group-Object
cmdlet. In its simplest form, you specify one property on which your objects will be grouped.
Get-VM | Group-Object -Property GuestId
Count Name Group
----- ---- -----
1 rhel7_64Guest {VM6}
1 sles12_64Guest {VM3}
1 windows7Server64Guest {VM5}
2 windows8Server64Guest {VM2, VM1}
1 windows7_64Guest {VM4}
Notice that the objects produced by the Group-Object
cmdlet are not the original objects as returned by the Get-VM
cmdlet. The Group-Object
cmdlet creates a new object, with specific properties like Count
and Name
. The actual objects that came out of the Get-VM
cmdlet are all placed in an array under the Group
property. This allows you to fetch individual properties from the objects in the group.
Get-VM |
Group-Object -Property GuestId |
Select Name,Count,
@{N='VMHost';
E={[string]::Join(',',($_.Group | Select -ExpandProperty VMHost))}} |
Format-Table -AutoSize
Name Count VMHost
---- ----- ------
rhel7_64Guest 1 esx61.local.test
sles12_64Guest 1 esx61.local.test
windows7Server64Guest 1 esx62.local.test
windows8Server64Guest 2 esx61.local.test,esx61.local.test
windows7_64Guest 1 esx61.local.test
Here we used a calculated property to list all the ESXi nodes on which the VirtualMachines
in a specific group are located. Since the VMHost property for a group can have multiple ESXi nodes, the example uses the .NET Join
method to convert the individual values into one string.
As you should be aware by now, the PowerCLI Get
cmdlets produce objects (most of the time). These objects contain a number of properties that were selected by the PowerCLI development team. These properties were selected in such a way that, for most of the common use cases, required properties are present in the object. If you encounter a situation where this is not the case, don’t despair. There are many ways to extend objects with new properties.
To extend an existing object, you can use the Add-Member
cmdlet. This cmdlet adds a user-defined property to an instance of a PowerShell object. Remember that Add-Member
only adds the property to a specific object instance—and not to the object’s definition. Any new objects that you create won’t have the newly added property. If you want the property to be available in all new instances, you’ll need to modify the object’s definition in the Types.ps1xml
file. This file, which is located in the PowerShell installation directory, is digitally signed to prevent tampering, but you can create your own Types.ps1xml
file to further extend the types. Extending object definitions is beyond the scope of this book. If you want to modify the object types, start by looking at the PowerShell built-in about_Types.ps1xml
Help topic:
Help about_Types.ps1xml
Now, let’s use the Add-Member
cmdlet to add a numVM
property to the cluster object that holds the number of virtual machines:
$clusterReport = @()
foreach ($cluster in Get-Cluster) {
$cluster | Add-Member -MemberType NoteProperty -Name numVM '
-Value @($cluster | Get-VM).count
$clusterReport += $cluster
}
$clusterReport | Select Name, numVM
When your reporting requirements call for information from different objects, it’s much easier to create your own custom object rather than extend an existing object. The best way to create a report like this is to define a custom object that includes all the properties you need in your report. A custom object can be created using the New-Object
cmdlet and properties can be defined with the Add-Member
cmdlet:
$myObject = New-Object Object
$myObject | Add-Member –MemberType NoteProperty '
–Name Vm –Value $null
$myObject | Add-Member –MemberType NoteProperty '
–Name HostName –Value $null
$myObject | Add-Member –MemberType NoteProperty '
–Name ClusterName –Value $null
One of the features that introduced in PowerCLI 4.1 is the New-VIProperty
cmdlet. This cmdlet lets you add your own properties to a specified PowerCLI object type. Because you are changing the object type (and not just a single existing instance like the Add-Member
cmdlet does), the new property will be available on the next retrieval of the corresponding objects. Let’s illustrate this with an example.
Get-VM | Get-View | Select-Object Name, @{Name="ToolsVersion";
Expression={$_.Config.Tools.ToolsVersion}}
Using that script, you are forced to use the Get-View
cmdlet to access the underlying SDK object to retrieve the Tools version. Using the New-VIProperty
cmdlet, you can create a new property to hold this information, so you don’t need to retrieve the underlying SDK anymore:
New-VIProperty -Name toolsVersion -ObjectType VirtualMachine '
-ValueFromExtensionProperty 'Config.Tools.ToolsVersion'
Get-VM | Select Name, toolsVersion
You’ll notice that the code using the New-VIProperty
cmdlet is much faster. This is because you don’t have to fetch the complete SDK object using the Get-View
cmdlet.
Numerous sources of information are available to report on your vSphere environment. Some of these are easy to access; others will require a bit more effort. In this section, we will describe the major information sources that you can use in your reporting.
The PowerCLI objects are the objects returned by the PowerCLI cmdlets. As we stated earlier, these objects contain a set of properties that were selected by the PowerCLI development team. The selection of these properties was done in such a way that the most commonly used ones are directly available in the PowerCLI objects. To find out what is available, you can use the Get-Member
cmdlet. It lists the available properties and property types for a PowerCLI object.
Get-VMHost -Name esx61.local.test | Get-Member -MemberType Property
TypeName: VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl
Name MemberType Definition
---- ---------- ----------
ApiVersion Property string ApiVersion {get;}
Build Property string Build {get;}
Another useful method to investigate what is in the PowerCLI objects is through the use of the Format-Custom
cmdlet. This cmdlet shows all nested properties, as far as you define on the Depth
parameter. The cmdlet shows the values, if present, for each of the properties.
Get-Datastore -Name DS10 | Format-Custom -Depth 2
WARNING: The 'Accessible' property of Datastore type is deprecated
Use the 'State' property instead
.
class VmfsDatastoreImpl
{
FileSystemVersion = 5.61
DatacenterId = Datacenter-datacenter-2
Datacenter =
class DatacenterImpl
{
ParentFolderId = Folder-group-d1
ParentFolder =
class FolderImpl
{
ParentId =
This can help if you are trying to find a property that holds a specific value. Just scroll through the generated output until you find the targeted value. The drawback is that the objects coming out of the Format-Custom
cmdlet are intended for the output engine. It is not plain text that you could search with a -like
or -match
operator.
To help automate your searches of PowerCLI objects, you can use the ConvertTo-Text
function and “dump” in text format to the specific depth the content of any object you feed it (see Listing 15-1). That way, you can easily search the PowerCLI object.
Listing 15-1: Dumping an object as text
function ConvertTo-Text {
<#
.SYNOPSIS
Convert an object to text
.DESCRIPTION
This function takes an object and converts it to a textual
representation.
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER InputObject
The object to be represented in text format
.PARAMETER Depth
Defines how 'deep' the function shall traverse the object.
The default is a depth of 2.
.PARAMETER FullPath
A switch that defines if the property is displayed with
indentation or with the full path
.PARAMETER ExtensionData
A switch that defines if the ExtensionData property shall
be handled or not
.EXAMPLE
Get-VM -Name VM1 | ConvertTo-Text -Depth 2
.EXAMPLE
ConvertTo-Text -InputObject $esx -FullPath
#>
#Requires -Version 4.0
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$True)]
[object[]]$InputObject,
[int]$Depth = 2,
[switch]$FullPath = $false,
[Switch]$ExtensionData = $false,
[Parameter(DontShow)]
[string]$Indent = '',
[Parameter(DontShow)]
[string]$Path = ''
)
Process{
if($Indent.Length -lt $Depth){
foreach($object in $InputObject){
$object.PSObject.Properties | Foreach-Object -Process {
if($FullPath){
"$($Path + '' + $_.Name) - $($_.TypeNameOfValue) = $($_.Value)"
}
else{
"$($Indent)$($_.Name) - $($_.TypeNameOfValue) = $($_.Value)"
}
if(($_.Name -ne 'ExtensionData' -or $ExtensionData) -and '
$_.PSObject.Properties){
$ctSplat = @{
InputObject = $object."$($_.Name)"
Depth = $Depth
FullPath = $FullPath
Path = "$($Path + '' + $_.Name)"
Indent = " $($Indent)"
ExtensionData = $ExtensionData
}
ConvertTo-Text @ctSplat
}
}
}
}
}
}
You can explore any PowerCLI object with this function, but be warned that the function might produce a long list of text. Some PowerCLI objects are quite complex and contain a lot of nested objects.
Since a PowerCLI object can now be dumped as text, you can use all the text search functionality that PowerShell offers. If you want to know, for example, where in a VirtualMachine
object the MAC address of the NIC is stored, you can try the following:
$vm = Get-VM -Name vm1
$vm | ConvertTo-Text -Depth 2 -FullPath |
where {$_ -match '00:50:56:bc:7c:2c'}
NetworkAdaptersMacAddress - System.String = 00:50:56:bc:7c:2c
From the result, you find that the MAC address is stored under the NetworkAdapters
property.
You do not always have to start at the root of a PowerCLI object; you can point to a nested property:
ConvertTo-Text -InputObject $esx.StorageInfo.ScsiLun -Depth 2 |
where {$_ -match 'LunType'}
LunType - System.String = disk
LunType - System.String = disk
LunType - System.String = cdrom
LunType - System.String = disk
Based on that output, you can deduce that on this ESXi node three disk LUNs and one CD-ROM drive are connected.
The vSphere View objects are the objects that are used by vSphere internally. These objects are documented in the VMware vSphere API Reference, and will be discussed at greater length in Chapter 18, “The SDK.” vSphere View objects are presented to PowerCLI in two ways:
ExtensionData
property in several places in the PowerCLI objectsGet-View
cmdletIn both cases, this representation of the vSphere View objects is a read-only copy of the actual vSphere object. This means that the content of the properties is not updated automatically. You will have to get the object again or use the UpdateViewData
method.
$vm.ExtensionData.Config.Hardware.NumCPU
1
Set-VM -VM $vm -NumCpu 2 -Confirm:$false
Name PowerState Num CPUs MemoryGB
---- ---------- -------- --------
VM1 PoweredOff 2 4.000
$vm.ExtensionData.Config.Hardware.NumCPU
1
$vm.ExtensionData.UpdateViewData()
$vm.ExtensionData.Config.Hardware.NumCPU
2
The ConvertTo-Text
function from Listing 15-1 can also be used on vSphere View objects. The following short sample searches under the Config
property of an ESXi node for the location where IPv6Enabled
can be found.
ConvertTo-Text -InputObject $esx.ExtensionData.Config -Depth 2 |
where {$_ -match 'IpV6Enabled'}
IpV6Enabled - System.Nullable'1[[System.Boolean, mscorlib, Version=4.0.0.0,
Culture=neutral, P
ublicKeyToken=b77a5c561934e089]] = False
AtBootIpV6Enabled - System.Nullable'1[[System.Boolean, mscorlib,
Version=4.0.0.0, Culture=neut
ral, PublicKeyToken=b77a5c561934e089]] = False
On the ESXi console there are many commands available to configure and display the ESXi settings. The esxcli
command, which is by far the most important console command, can easily be accessed from within a PowerCLI session. Other console commands will require you to establish a Secure Shell (SSH) connection to the ESXi server.
With the Get-EsxCli
cmdlet you have access to the functionality that is available in all the flavors of the esxcli
command. The main difference is that you don’t need to SSH into the ESXi console.
To use the esxcli
commands via PowerCLI, you first have to set up the esxcli
object via the Get-EsxCli
cmdlet.
$esx = Get-VMHost -Name esx1.local.test
$esxcli = Get-EsxCli -VMHost $esx
Once you have this object, you can navigate through all available namespaces and work with the available commands in each of the namespaces. In the next example, we are in the system.version
namespace and use the get()
method to obtain information regarding the ESXi version and build:
$esxcli.system.version.get()
Build : Releasebuild-2494585
Patch : 0
Product : VMware ESXi
Update : 0
Version : 6.0.0
There are quite a few available namespaces, but for some of the methods in these namespaces, it is not obvious which parameters to use. To help with that, the Get-EsxCliCommand
function in Listing 15-2 returns all the namespaces, the available methods, and the parameters for each method, and it includes Help text for each.
Listing 15-2: Returning details for each Get-EsxCli
namespace
function Get-EsxCliCommand {
<#
.SYNOPSIS
Returns all available namespaces through the Get-EsxCli object
.DESCRIPTION
The Get-EsxCli cmdlet returns an object that can be used to
access all properties and methods that are available. This
function retuns all available methods in these namespaces,
together with a short help text and the method's parameters.
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER VMHost
The host for which to list the esxcli namespaces
.EXAMPLE
Get-EsxCliCommand -VMHost $esx
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[PSObject]$VMHost
)
Process{
$esxcli = Get-EsxCli -VMHost $VMHost
$esxcli.esxcli.command.list() | %{
$steps = $_.NameSpace.Split('.') + $_.Command
$stepsHelp = $_.NameSpace.Split('.') + 'help'
# Get the namespace object
$nSpace = $esxcli
$steps | %{
$nSpace = $($nSpace.$($_))
}
# Get the namespace Help object
$helpObject = $esxcli
$stepsHelp | %{
$helpObject = $($helpObject.$($_))
}
# Fetch the help for the method
if($helpObject){
$helpObjectelp = $helpObject.Invoke($_.Command)
}
else{
$helpObjectelp = $null
}
# Method details
$_ | Select @{N='Command';E={"'$esxcli.$($_.NameSpace).$($_.Command)"}},
@{N='Syntax/Parameter';E={"$($nSpace.Value.ToString())"}},
@{N='Help';E={$helpObjectelp.Help}}
# Parameter details
if($helpObject){
$helpObjectelp.Param | Select @{N='Command';E={''}},
@{N='Syntax/Parameter';E={$_.DisplayName}},
@{N='Help';E={$_.Help}}
}
}
}
}
You call the function with a VMHost
parameter, and you can redirect the returned information to a file or display it onscreen (see Figure 15-1).
Get-EsxCliCommand -VMHost $esxName | Out-GridView
This information should make it easier to use esxcli
namespace methods. In Figure 15-1 you can see, for example, how the set
method in the hardware.cpu.global
namespace takes one parameter of type Boolean.
When the Get-EsxCli
namespaces do not provide access to the command or data you want to access, you can always fall back on establishing a SSH connection to the limited ESXi console based on BusyBox
. To allow the SSH connection, you first have to make sure the SSH service is running on the ESXi node. This can be done with just a few cmdlets.
$esxNames = 'esx1.local.test','esx2.local.test'
Get-VMHost -Name $esxNames | Get-VMHostService |
Where {$_.Key -eq "TSM-SSH"} |
Start-VMHostService -Confirm:$false
Once the SSH service is running, you need to establish a SSH connection from your script to the ESXi console. One popular way of doing this is with the help of the plink.exe
application that is part of the PuTTY Suite available from
www.chiark.greenend.org.uk/~sgtatham/putty/
This free collection of tools offers all kinds of functionality based on SSH.
The plink.exe
application allows you to send a bash script to the ESXi node and capture the returned data.The following function (Listing 15-3) allows you, in a simple way, to use plink.exe
to send commands to the ESXi console and get the output of that command in your script.
Listing 15-3: Running a command via SSH with plink.exe
function Invoke-EsxSSH {
<#
.SYNOPSIS
Execute an ESXi console command through an SSH session
.DESCRIPTION
This function is a wrapper for the plink.exe command in the
PuTTY Suite.
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER VMHost
The host on which to execute a command
.PARAMETER Command
The command to be executed
.PARAMETER Credential
The credential to login to the host
.PARAMETER PathExe
The path to the folder where the PuTTY Suite is stored
.PARAMETER IncludeError
A switch to define if the returned output of the command
should include eventual error messages
.EXAMPLE
PS> $esxsshSplat = @{
VMHost = $esx
Command = $cmd
Credential = $cred
PathExe = 'C:PuTTY'
}
PS> Invoke-EsxSSH @esxsshSplat
.EXAMPLE
Get-VMHost | Invoke-EsxSSH -Command $cmd -Credential $cred
#>
[CmdLetBinding()]
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[PSObject]$VMHost,
[Parameter(Mandatory=$true)]
[String]$Command,
[Parameter(Mandatory=$true)]
[System.Management.Automation.PSCredential]$Credential,
[String]$PathExe = 'C:Putty',
[Switch]$IncludeError = $false
)
Begin{
if(Test-Path -Path "$($PathExe)plink.exe"){
$plink = "$($Path)plink.exe"
}
else{
Throw "Application plink.exe not found in $($PathExe)"
}
}
Process{
ForEach($esx in $VMHost){
if($esx -is [System.String]){
$esx = Get-VMHost -Name $esx
}
$user = $Credential.UserName
$password = $Credential.GetNetworkCredential().password
$esxName = $esx.Name
$plinkoptions = "-v -pw $password"
$plink = "$($PathExe)plink.exe"
$value = "rsa2@22:$($esx.Name)"
$userInfo = $user,$esxName -join '@'
$cmd = $plink,$plinkoptions,$userInfo,$Command,'2>&1' -join ' '
Try{
$branch = 'HKCU:SoftwareSimonTathamPuttySshHostKeys'
Get-ItemProperty -Path $branch -Name $value -ErrorAction Stop |
Out-Null
}
Catch{
$confirmation = 'Write-Output -InputObject "y" |'
$cmd = $confirmation,$cmd -join ' '
}
$output = Invoke-Expression -Command $cmd
if($IncludeError){
$output
}
else{
$output |
Where{$_ -isnot [System.Management.Automation.ErrorRecord]}
}
}
}
}
The use of this function is quite straightforward, as the following example will show. In the example, we use the partedUtil
command to get the partition layout of the system disk used by ESXi.
$esx = 'esx1.local.test'
$User = 'root'
$pswd = 'passoword'
$cmd = '/bin/partedUtil getptbl /vmfs/devices/disks/mpx.vmhba1:C0:T0:L0'
$secpswd = ConvertTo-SecureString $Pswd -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($User,$secpswd)
Invoke-EsxSSH -VMHost $esx -Command $cmd -Credential $cred -PathExe 'C:PuTTY'
The returned data will look something like this:
gpt
1044 255 63 16777216
1 64 8191 C12A7328F81F11D2BA4B00A0C93EC93B systemPartition 128
5 8224 520191 EBD0A0A2B9E5443387C068B6B72699C7 linuxNative 0
6 520224 1032191 EBD0A0A2B9E5443387C068B6B72699C7 linuxNative 0
7 1032224 1257471 9D27538040AD11DBBF97000C2911D1B8 vmkDiagnostic 0
8 1257504 1843199 EBD0A0A2B9E5443387C068B6B72699C7 linuxNative 0
9 1843200 7086079 9D27538040AD11DBBF97000C2911D1B8 vmkDiagnostic 0
2 7086080 15472639 EBD0A0A2B9E5443387C068B6B72699C7 linuxNative 0
3 15472640 16777182 AA31E02A400F11DB9590000C2911D1B8 vmfs 0
You can use the data to report on the partition layout and the disk space usage in each of these partitions. Further details on how this is done can be found in KB 1036609:
With tasks and events, you can report on all activity that takes place in your vSphere environment. The tasks and events data contains everything you see in the Tasks & Events tab in vSphere Client—and more. In all inventory views, vSphere Client offers two ways of looking at tasks and events. In Tasks view, you can view a list of all the tasks (upper pane), with the related events for each task (lower pane).
There are two sources of tasks and events available on vSphere servers:
To check what is currently configured on your vCenter, use the following:
Get-AdvancedSetting -Entity $global:DefaultVIServer |
where {$_.Name -match '^task|^event' } |
Select Name,Value
Name Value
---- -----
event.maxAge 100
event.maxAgeEnabled True
task.maxAge 100
task.maxAgeEnabled False
On this specific vCenter the events are retained for 100 days, but the tasks are not retained. With the Set-AdvancedSetting
cmdlet, you can change these settings. You can do something like this:
$tab = @{
'event.maxAgeEnabled'=$true
'event.maxAge'=365
'task.maxAgeEnabled'=$true
'task.maxAge'=365
}
$tab.GetEnumerator() | %{
Get-AdvancedSetting -Entity $global:DefaultVIServer -Name $_.Name |
Set-AdvancedSetting -Value $_.Value -Confirm:$false |
Select Name,Value
}
Name Value
---- -----
event.maxAge 365
event.maxAgeEnabled True
task.maxAgeEnabled True
task.maxAge 365
Notice how we used a hash table to define the Name
and Value
for the advanced settings.
The Get-Enumerator
method that is available on the hash table will return each key-value pair that exists in the hash table. The key-value pairs are then passed, over the pipeline, to the Foreach-Object
code block. In that code block, the AdvancedSetting
, defined in the Name
property, is updated set to the new value that was specified in the Value
property.
The function in Listing 15-4 allows you to get an overview of all the events that are present in your vSphere environment. Note that the number of returned events depends on the components you have installed in your vSphere environment.
Listing 15-4: The Get-VIEventType
function
function Get-VIEventType {
<#
.SYNOPSIS
Returns all the available event types in the vSphere environment
Can be used on against a vCenter and an ESXi server
.DESCRIPTION
The function returns a string array that contains all the
available event types in the current vSphere environment.
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER Category
Select the event type to report on.
Default values are: info,warning,error,user
.EXAMPLE
Get-VIEventType | Export-Csv -Path $csvName -NoTypeInformation
#>
Param(
[parameter(HelpMessage = "Accepted categories: info,warning,error,user")]
[ValidateSet("info","warning","error","user")]
[string[]]$Category = @("info","warning","error","user"))
begin{
$si = Get-View ServiceInstance
$eventMgr = Get-View $si.Content.EventManager
}
process{
$eventMgr.Description.EventInfo |
Where-Object {$Category -contains $_.Category} | Foreach-Object {
New-Object PSObject -Property @{
Name = $_.Key
Category = $_.Category
Description = $_.Description
Hierarchy = &{
$obj = New-Object -TypeName ("VMware.Vim." + $_.Key)
if($obj){
$obj = $obj.GetType()
$path = ""
do{
$path = ($obj.Name + "/") + $path
$obj = $obj.BaseType
} until($path -like "Event*")
$path.TrimEnd("/")
}
else{
"--undocumented--"
}
}
}
}
}
}
The simplest use of the Get-VIEventType
function returns all the events known in your vSphere environment. Notice how the Hierarchy
property shows how each event was derived from the base class Event
:
Get-VIEventType | fl
Name : AccountCreatedEvent
Hierarchy : Event/HostEvent/AccountCreatedEvent
Description : Account created
Category : info
Name : AccountRemovedEvent
Hierarchy : Event/HostEvent/AccountRemovedEvent
Description : Account removed
Category : info
Name : AccountUpdatedEvent
Hierarchy : Event/HostEvent/AccountUpdatedEvent
Description : Account updated
Category : info
You will also find some undocumented, and hence unsupported, events in the list:
Name : ChangeOwnerOfFileEvent
Hierarchy : --undocumented--
Description : Change owner of file
Category : info
Name : ChangeOwnerOfFileFailedEvent
Hierarchy : --undocumented--
Description : Cannot change owner of file name
Category : error
And you will also encounter several of the special event types, ExtendedEvent
and EventEx
:
Name : ExtendedEvent
Hierarchy : Event/GeneralEvent/ExtendedEvent
Description : com.vmware.vcIntegrity.VMToolsNotRunning
Category : error
Name : EventEx
Hierarchy : EventEx
Description : Lost Network Connectivity
Category : error
There are a number of ways to retrieve the events for your vSphere environment.
This cmdlet retrieves the events from a vSphere server. This vSphere server can be a vCenter server or an ESXi host. See the Help topic for the details on how to use this cmdlet.
The following two examples illustrate how the SDK API can be used to enhance the retrieval of events.
The Get-Task
cmdlet allows you to query tasks only in a specific state with the Status
parameter. In some situations, it might be useful to be able to query tasks within a specific time frame. You might also want to find tasks that ran against specific entities. Listing 15-5 shows how you can retrieve tasks that ran against a specific entity using Get-VITaskSDK
.
Listing 15-5: Retrieving tasks that ran against a specific entity
function Get-VITaskSDK {
<#
.SYNOPSIS
Returns Tasks that comply with the specifications passed
in the parameters
.DESCRIPTION
The function will return vSphere tasks, as TaskInfo objects,
that fit the specifications passed through the parameters.
A connection to a vCenter is required!
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER Entity
The entity whose tasks shall be returned
.PARAMETER EntityChildren
A switch that specifies if the tasks for the Entity or for
the Entity and all its children shall be returned
.PARAMETER Start
The beginning of the time range in which to look for tasks.
If not specified, the function will start with the oldest
available task.
.PARAMETER Finish
The end of the time range in which to look for tasks.
If not specified, the function will use the current time
as the end of the time range.
.PARAMETER State
The state of the tasks. Valid values are error, queued,
running, success
.PARAMETER User
If specified will only return tasks started by this user.
If not specified the function will return tasks started by
any user.
.EXAMPLE
Get-Cluster -Name "MyCluster" | Get-VITaskSDK
.EXAMPLE
Get-VITaskSDK -Entity (Get-VM myVM) -State "error"
#>
Param(
[parameter(Mandatory=$true,ValueFromPipeline = $true)]
$Entity,
[switch]$EntityChildren = $false,
[DateTime]$Start,
[DateTime]$Finish,
[ValidateSet('error','queued','running','success')]
[string]$State,
[string]$User
)
Begin{
if($defaultVIServer.ProductLine -ne "vpx"){
Throw 'Error : you need to be connected to a vCenter'
}
$taskMgr = Get-View TaskManager
$taskNumber = 100
}
Process{
$Entity | Foreach-Object{
$taskFilter = New-Object VMware.Vim.TaskFilterSpec
$taskFilter.Entity = New-Object VMware.Vim.TaskFilterSpecByEntity
$taskFilter.Entity.entity = $_.ExtensionData.MoRef
if($EntityChildren){
$taskFilter.Entity.recursion = 'all'
}
else{
$taskFilter.Entity.recursion = 'self'
}
}
if($Start -or $Finish){
$taskFilter.Time = '
New-Object VMware.Vim.TaskFilterSpecByTime
if($Start){
$taskFilter.Time.beginTime = $Start
}
if($Finish){
$taskFilter.Time.endTime = $Finish
$taskFilter.Time.timeType = 'startedTime'
}
}
if($State){
$taskFilter.State = $State
}
if($User){
$taskFilter.UserName = $User
}
$taskCollectorMoRef = $taskMgr.CreateCollectorForTasks($taskFilter)
$taskCollector = Get-View $taskCollectorMoRef
$taskCollector.RewindCollector | Out-Null
$tasks = $taskCollector.ReadNextTasks($taskNumber)
while($tasks){
$tasks | Foreach-Object {
$_
}
$tasks = $taskCollector.ReadNextTasks($taskNumber)
}
# By default 32 task collectors are allowed.
# Destroy this task collector.
$taskCollector.DestroyCollector()
}
}
The Get-VIEvent
cmdlet returns all events within a specific time frame. Sometimes, it could be handy to retrieve only events that were produced by a specific entity, eventually including all the entity’s children. The function in Listing 15-6 can be run against either a vCenter server or an ESXi server. When you run it against an ESXi server, it returns the last 1,000 events or all events since the last reboot of the server if there are fewer than 1,000 events.
Listing 15-6: Retrieving events produced by specific entities
function Get-VIEventSDK {
<#
.SYNOPSIS
Returns Events that comply with the specifications passed
in the parameters
.DESCRIPTION
The function will return vSphere events, as Event objects,
that fit the specifications passed through the parameters.
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER Entity
The entity whose events shall be returned
.PARAMETER EntityChildren
A switch that specifies if the events for the Entity or for
the Entity and all its children shall be returned
.PARAMETER Start
The beginning of the time range in which to look for events.
If not specified, the function will start with the oldest
available event.
.PARAMETER Finish
The end of the time range in which to look for events.
If not specified, the function will use the current time
as the end of the time range.
.PARAMETER EventChainId
The function will only return events that have this specific
EventChainId.
.PARAMETER User
If specified will only return events for tasks triggered by
this user. If not specified the function will return events
independent of the user that started the task.
.EXAMPLE
Get-VIEventSDK -Entity (Get-Cluster -Name "MyCluster")
.EXAMPLE
Get-VM myVM | Get-VIEventSDK -EventChainId $task.EventChainId
#>
Param(
[parameter(Mandatory=$true,ValueFromPipeline = $true)]
$Entity,
[switch]$EntityChildren = $false,
[DateTime]$Start,
[DateTime]$Finish,
[Int]$EventChainId,
[String]$User
)
Begin{
$si = Get-View ServiceInstance
$eventMgr = Get-View $si.Content.EventManager
$eventNumber = 100
}
Process{
$Entity | ForEach-Object -Process {
$eventFilter = New-Object VMware.Vim.EventFilterSpec
if($Entity){
$eventFilter.Entity = '
New-Object VMware.Vim.EventFilterSpecByEntity
$eventFilter.Entity.entity = '
($Entity | Get-View).MoRef
if($EntityChildren){
$eventFilter.Entity.recursion = "all"
}
else{
$eventFilter.Entity.recursion = "self"
}
}
if($Start -or $Finish){
$eventFilter.Time = '
New-Object VMware.Vim.EventFilterSpecByTime
if($Start){
$eventFilter.Time.beginTime = $Start
}
if($Finish){
$eventFilter.Time.endTime = $Finish
$eventFilter.Time.timeType = "startedTime"
}
}
if($EventChainId){
$eventFilter.eventChainId = $EventChainId
}
if($User){
$taskFilter.UserName = $User
}
$eventCollectorMoRef = $eventMgr.CreateCollectorForEvents($eventFilter)
$eventCollector = Get-View $eventCollectorMoRef
$eventCollector.RewindCollector | Out-Null
$events = $eventCollector.ReadNextEvents($eventNumber)
while($events){
$events | Foreach-Object {
$_
}
$events = '
$eventCollector.ReadNextEvents($eventNumber)
}
# By default 32 task collectors are allowed.
# Destroy this task collector.
$eventCollector.DestroyCollector()
}
}
}
The flow of these two functions is quite similar:
while
loop to retrieve all the objects that fit the specifications. Notice that these methods use a kind of sliding window that scrolls through all the tasks and events.A lot of information is available on the performance of all the components in your vSphere environment. How to access this performance data, and how to create reports from the data, is discussed in more detail in Chapter 16, “Using Statistical Data.”
With PowerShell 3, a number of CIM cmdlets were introduced. These CIM cmdlets allow interaction with the Common Information Model (CIM), an open standard for querying managed objects in an IT environment. VMware uses CIM on the ESXi hypervisor to monitor the hardware on the server. This information is passed to the CIM Broker. You can see this information under Configuration - Health Status when connected to an ESXi server and under Hardware Status when connected to a vCenter.
The CIM classes provided by VMware are documented in the VMware CIM SMASH/Server Management API Reference:
Listing 15-7, based on a function published by Carter Shanklin former PowerCLI Product Manager, wraps a call to the ESXi CIM API.
Listing 15-7: CIM API wrapper
function Get-VMHostCimInstance {
<#
.SYNOPSIS
Access the CIM interface of an ESXi node
.DESCRIPTION
A wrapper function that calls the CIM interface of an ESXi
node.
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Brian Graf, Arnim van Lieshout,
Jonathan Medd, Alan Renouf, Glenn Sizemore,
Andrew Sullivan
.PARAMETER HostName
The hostname or IP address of the ESXi node
.PARAMETER Class
The CIM class to query
.PARAMETER IgnoreCertFailures
A switch to define if certificate errors shall be ignored
.PARAMETER Credential
The credentials to login to the ESXi host.
.EXAMPLE
PS> $cimSplat = @{
HostName = '192.168.1.1'
Class = 'VMware_PCIDevice'
IgnoreCertFailures = $true
Credential = $cred
}
PS> Get-VMHostCimInstance @cimSplat
#>
[CmdletBinding()]
Param (
[String]$HostName,
[Parameter(ValueFromPipeline=$true)]
[String]$Class,
[Switch]$IgnoreCertFailures,
[System.Management.Automation.PSCredential]$Credential
)
begin {
Try {
Get-Module -Name Cimcmdlets -ErrorAction Stop | Out-Null
}
Catch {
Import-Module -Name CimCmdLets
}
$optCIMParams = @{
Encoding = "Utf8"
UseSsl = $true
MaxEnvelopeSizeKB = 1024
}
if($IgnoreCertFailures){
$optCIMParams.Add("SkipCACheck",$true)
$optCIMParams.Add("SkipCNCheck",$true)
$optCIMParams.Add("SkipRevocationCheck",$true)
}
$optCIM = New-CimSessionOption @optCIMParams
$sessionCIMParams = @{
Authentication = "Basic"
Credential = $Credential
ComputerName = $HostName
Port = 443
SessionOption = $optCIM
}
$session = New-CimSession @sessionCIMParams
}
process {
$Class | Foreach-Object {
$instanceCIMParams = @{
CimSession = $session
}
if($_ -match "^CIM_"){
$instanceCIMParams.Add("ClassName",$_)
}
if($_ -match "^OMC_"){
$instanceCIMParams.Add("ResourceUri",
"http://schema.omc-project.org/wbem/wscim/1/cim-schema/2/$_")
}
if($_ -match "^VMware_"){
$instanceCIMParams.Add("ResourceUri",
"http://schemas.vmware.com/wbem/wscim/1/cim-schema/2/$_")
}
Get-CimInstance @instanceCIMParams
}
}
end {
Remove-CimSession -CimSession $session
}
}
With this function, you can now easily query any CIM class and extract information. The next example queries for all PCI devices on an ESXi server:
$ipaddress = 'esx1.local.test'
$username = 'root'
$pswd = 'password'
$pswdSecure = ConvertTo-SecureString -String $pswd -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential '
-ArgumentList $username,$pswdSecure
Get-VMHostCimInstance -Class 'VMware_PCIDevice' -HostName $ipaddress '
-IgnoreCertFailures -Credential $cred |
Select DeviceID,
@{N='VendorID';E={'0x{0:x4}' -f [int]$_.VendorID}},
@{N='PCIDeviceID';E={'0x{0:x4}' -f [int]$_.PCIDeviceID}},
@{N='Name';E={'{0,-15}' -f $_.ElementName}} |
Format-Table -AutoSize
The returned result looks something like this.
DeviceID VendorID PCIDeviceID Name
-------- -------- ----------- ----
PCI 0:0:7:1 0x8086 0x7111 Intel Corporation PIIX4 for 430TX/…
PCI 0:0:7:7 0x15ad 0x0740 VMware Virtual Machine Communicati…
PCI 0:0:15:0 0x15ad 0x0405 VMware SVGA II Adapter #15
PCI 0:0:16:0 0x1000 0x0030 LSI Logic / Symbios Logic 53c1030 …
PCI 0:2:0:0 0x8086 0x100f Intel Corporation 82545EM Gigabit …
PCI 0:2:1:0 0x8086 0x100f Intel Corporation 82545EM Gigabit …
PCI 0:2:2:0 0x8086 0x100f Intel Corporation 82545EM Gigabit …
PCI 0:2:3:0 0x8086 0x100f Intel Corporation 82545EM Gigabit …
An attentive reader might have noticed that this ESXi node is obviously a nested ESXi. That explains the VMware PCI devices in the list.
You can, of course, use any kind of data in your reports, and numerous other sources are available that are directly or indirectly linked to your vSphere environment. Several of the vSphere family products come, for example, with a REST interface. With PowerShell and the Invoke-WebRequest
cmdlet (make sure you are using at least PowerShell 4), it is easy to access REST interfaces.
Once you have collected the data for your report, you will need to present it in one form or another. With PowerShell, many formats for presenting your data are available.
This is the simplest way to visualize results from a script. As you have seen in previous examples in this chapter, when the Select-Object
cmdlet is used and you do not redirect the output to another destination, the results will be displayed in the console of the PowerShell session.
If you want to quickly view your report in a nice interactive table, you can use the Out-GridView
cmdlet. Remember that this feature requires Microsoft .NET Framework 3.5 with Service Pack 1 to work. Using the interactive table, you can sort your data on any column by clicking the column header. To hide, show, or reorder columns, right-click a column header. You can also apply a search filter or define criteria by unfolding the Query window and adding criteria using the Add button. Figure 15-2 shows a typical Out-GridView
report.
From within PowerShell you can create multiple file formats. The following sections show how to use some of the more commonly used file types.
Plain text is the simplest way to produce output from your scripts. In the previous examples in this chapter, several used a Select-Object
to display the results. By default, this output will appear on the console of your PowerShell session.
You can export your report to a CSV file using the Export-Csv
cmdlet. This way, the report can be easily imported into a spreadsheet program, like Microsoft Excel:
$report | Export-Csv c: empMyReport.csv –NoTypeInformation
By default, the first line of the CSV file contains #TYPE
followed by the fully qualified name of the type of the .NET Framework object. To omit the type information, use the -NoTypeInformation
parameter.
You can also produce Excel spreadsheets from your PowerShell scripts. There are several functions around that produce XLSX files. One example is the Export-Xlsx
function, which is published on LucD Notes here:
www.lucd.info/2013/01/03/export-xls-the-sequel-and-ordered-data/
When you’re scheduling reports to run on a regular basis, it is always nice to receive the generated report in your inbox. From within PowerShell, you can send an email using the Send-MailMessage
cmdlet. You can send a simple mail message that the report is finished; you can include the report as an attachment; or if you’ve created an HTML report, you can include it in the body of the mail message.
To email a fancy-looking HTML report to your manager:
Send-MailMessage -SmtpServer "[email protected]" '
-From "[email protected]" '
-To "[email protected]","[email protected]" '
-Subject "My management report" '
-Body $myHtmlReport –BodyAsHtml
To email some detailed reports as attachments to yourself:
Send-MailMessage -SmtpServer "[email protected]" '
-From "[email protected]" '
-To "[email protected]" '
-Subject "My detailed reports" '
-Body "Please review the attached detailed reports" '
-Attachments "c: emp
eport1.csv","c: emp
eport2.csv"
Although exporting to a CSV file is nice, if you want to do calculations, sorting, or filtering afterward, the format is not always well suited for a readable and distributable report. To create a more portable report, HTML is the preferred format. To create an HTML report, use the ConvertTo-Html
cmdlet. Let’s use an example NIC report and convert it into an HTML page like the one shown in Figure 15-3:
$nicReport=@()
foreach ($cluster in Get-Cluster) {
foreach ($vmHost in @($cluster | Get-VMHost)) {
foreach ($nic in @($VMHost | Get-VMHostNetworkAdapter)) {
$objNic = "" | Select ClusterName,HostName,Pci,DeviceName,Mac,BitRatePerSec,FullDuplex
$objNic.ClusterName = $cluster.Name
$objNic.HostName = $vmHost.Name
$objNic.Pci = $nic.ExtensionData.Pci
$objNic.DeviceName = $nic.DeviceName
$objNic.Mac = $nic.Mac
$objNic.BitRatePerSec = $nic.BitRatePerSec
$objNic.FullDuplex = $nic.FullDuplex
if ($nic.ExtensionData.Pci) {
$nicReport += $ObjNic
}
}
}
}
$nicReport | ConvertTo-Html > nicreport.html
The HTML report shown in Figure 15-3 is still very basic, but there are ways to go beyond the ordinary. Let’s make it a bit fancier. The ConvertTo-Html
cmdlet accepts several parameters that give you more flexibility in creating HTML output.
The -Body
parameter is used to specify content that appears directly after the <body>
tag and hence before the HTML table. This parameter is useful when you want to specify a header for your report:
$header = "<H2>Network Interface Card Report</H2>"
The -Head
parameter is used to specify the content of the <head>
tag. One very important thing that can be defined in the <head>
section is style information for the HTML document using the <style>
tag. Let’s create a custom <head>
section and define some fancy HTML styles, like those shown in Figure 15-4:
$myStyle = @"
<title>My Fancy Html Nic Report</title>
<style>
body {background-color: coral;}
table {border-collapse: collapse; border-width: 1px;
border-style: solid; border-color: black;}
tr {padding: 5px;}
th {border-width: 1px; border-style: solid; border-color: black;
background-color: blue; color: white;}
td {border-width: 1px; border-style: solid; border-color: black;
background-color: palegreen;}
</style>
"@
$nicReport |
ConvertTo-Html -Body $header -Head $myStyle > nicreport.html
The new report style shown in Figure 15-4 probably isn’t the best you’ve seen, but it gives you an idea of the possibilities. You’ll want your reports to use your company’s style. In that case, you can use the -CssUri
parameter to include your company’s CSS style sheet:
$nicReport |
ConvertTo-Html -CssUri c:mystylesheet.css > nicreport.html
If you don’t want to create a full HTML document but rather just an HTML table, use the -Fragment
parameter. That way, you can build your own custom HTML report and include multiple objects into the same report. In Listing 15-8, you find an example of how to create your own custom HTML report using data from two different reports, $nicReport
and $hbaReport
, combined into one. As you can see, the only limitation is your own imagination.
Listing 15-8: A custom HTML report
$html = @"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html><head>
<title>My Fancy Html Report</title>
<style>
body {background-color: coral;}
table {border-collapse: collapse; border-width: 1px;
border-style: solid; border-color: black;}
tr {padding: 5px;}
th {border-width: 1px; border-style: solid; border-color: black;
background-color: blue; color: white;}
td {border-width: 1px; border-style: solid; border-color: black;
background-color: palegreen;}
</style>
</head><body>
"@
$html += "<h2>Network Interface Cards</h2>"
$html += $nicReport | ConvertTo-Html -Fragment
$html += "<h2>Host Bus Adapters</h2>"
$html += $hbaReport | ConvertTo-Html -Fragment
$html += @"
</body>
</html>
"@
$html > nicreport.html
A great example of an HTML report is produced by the vCheck script.
You can find additional reports in Appendix A in this book. In Appendix A we tried to compile a series of useful reports that cover multiple aspects of a vSphere environment.
With the knowledge you obtained in this chapter, you should be able to easily adapt the sample scripts for your environments.