Script Extracts and Examples (PowerCLI): Difference between revisions
(→Certificates: Added -WarningAction) |
(→Useful CmdLets etc: Added "Virtual Centre") |
||
Line 123: | Line 123: | ||
== Useful CmdLets etc == | == Useful CmdLets etc == | ||
=== Virtual Centre === | |||
{|cellpadding="2" cellspacing="0" border="1" | |||
! Command !! Description | |||
|- | |||
| <code> (Get-View ServiceInstance).Content.About </code> | |||
| Get vCentre software version etc | |||
} | |||
=== Virtual Machine === | === Virtual Machine === | ||
Revision as of 15:27, 17 January 2011
Getting Started
Before being able to use any the following script you need to install...
On the first run you need to allow the Toolkit to run properly by running (you need to run this command as an administrator, so if you're using Windows 7, for example, you'll need to start the Powershell console as an administrator, regardless of whether you're currently logged in as an admin)...
Set-ExecutionPolicy RemoteSigned
Connect to the Virtual Centre (or ESX) server using the following command using your normal username and password (same as you'd use to log into the VI Client). You will need access to the servers on TCP 443.
Connect-VIServer -Server <server> -User <user> -Password <pass>
Once connected you can do real work with the PowerCLI, to see what's available, login to https://server/mob. This is effectively where you end up once you've completed the Connect-VIServer
command.
Be aware that PowerShell commands generally return objects, rather than text, and that the textual representation of the return object is often abbreviated for simplicity. To see the entire return for a command, pipe the result into Format-List
. To complicate matters further, some return objects contain further objects, see examples below
[vSphere PowerCLI] E:\> get-vm -name "vserver1"
Name PowerState Num CPUs Memory (MB)
---- ---------- -------- -----------
vserver1 PoweredOn 1 756
[vSphere PowerCLI] E:\> get-vm -name "vserver1" | Format-List *
PowerState : PoweredOn
Description :
Guest : VMware.VimAutomation.Client20.VMGuestImpl
NumCpu : 1
MemoryMB : 756
CDDrives : {CD/DVD Drive 1}
FloppyDrives : {Floppy Drive 1}
HardDisks : {Hard Disk 1}
NetworkAdapters : {Network Adapter 1}
Host : esx1.domain.com
HostId : HostSystem-host-301
HARestartPriority : ClusterRestartPriority
HAIsolationResponse : AsSpecifiedByCluster
DrsAutomationLevel : AsSpecifiedByCluster
CustomFields : {}
Id : VirtualMachine-vm-25136
Name : vserver1
[vSphere PowerCLI] E:\> get-vm -name "vserver2" | Format-List *
PowerState : PoweredOff
Description :
Guest : VMware.VimAutomation.Client20.VMGuestImpl
NumCpu : 2
MemoryMB : 2048
CDDrives : {CD/DVD Drive 1}
FloppyDrives : {Floppy Drive 1}
HardDisks : {Hard Disk 1}
NetworkAdapters : {Network Adapter 1, Network Adapter 2, Network Adapter 3}
Host : vserver2.domain.com
HostId : HostSystem-host-662
HARestartPriority : ClusterRestartPriority
HAIsolationResponse : AsSpecifiedByCluster
DrsAutomationLevel : AsSpecifiedByCluster
CustomFields : {}
Id : VirtualMachine-vm-697
Name : vserver2
[vSphere PowerCLI] E:\> get-vm -name "vserver2" | ForEach-Object {$_.NetworkAdapters}
MacAddress : 00:50:AB:CD:40:59
WakeOnLanEnabled : True
NetworkName : Some_Network
Type : Flexible
ConnectionState : VMware.VimAutomation.Client20.ConnectInfoImpl
Id : VirtualMachine-vm-697/4000
Name : Network Adapter 1
MacAddress : 00:50:AB:CD:55:40
WakeOnLanEnabled : True
NetworkName : Another_Network
Type : Flexible
ConnectionState : VMware.VimAutomation.Client20.ConnectInfoImpl
Id : VirtualMachine-vm-697/4001
Name : Network Adapter 2
MacAddress : 00:50:AB:CD:56:da
WakeOnLanEnabled : True
NetworkName : Yet_Another_Network
Type : Flexible
ConnectionState : VMware.VimAutomation.Client20.ConnectInfoImpl
Id : VirtualMachine-vm-697/4002
Name : Network Adapter 3
Installed Version
To determine the version of PowerCLI currently installed use;
Get-PowerCLIversion
Scheduling
To schedule a script to run you need to include the PowerCLI VIM console file so that PowerCLI Cmd-lets are available to your script. The command should use the following structure...
Powershell.exe -PSConsoleFile <vim.psc1> &<script.ps1>
For example, on a WinXP or Win2k3 (32bit) machine you might have something like...
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe -PSConsoleFile "C:\Program Files\VMware\Infrastructure\vSphere PowerCLI\vim.psc1" "& C:\Scripts\ESX-probe.ps1"
Or on a Win7 or Win2k8 (64bit) machine you might have something like...
Powershell -PSConsoleFile "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\vim.psc1" "& C:\Scripts\ESX-probe.ps1"
Certificates
A regular irritation is the certificate warnings that pop-up whenever you connect to a Virtual Centre. To completely fix you need update the default, out of the box, self-signed certificates that are created during the install with proper certs that can be tracked back to a fully trusted root (see http://www.vmware.com/pdf/vi_vcserver_certificates.pdf for further info).
To install a VC's certificate on your client machine (or any machine you want to run PowerCLI from and avoid the warnings)...
- Map a drive, or do whatever so you can access the VC's cert from the client machine
- Default location on Win2k3:
C:\Documents and Settings\All Users\Application Data\VMware\VMware VirtualCenter\SSL\rui.crt
- Default location on Win2k8:
C:\ProgramData\VMware\VMware VirtualCenter\SSL\rui.crt
- Default location on Win2k3:
- On the client machine, start the Certificate Manager (run
certmgr.msc
) - Right-click over Trusted Root Certificate Authorities, and click All Tasks > Import...
- Use the wizard to add the certificate
- Once added, go to the certificate's Properties and put your VC's hostname in the Friendly Name field
Alternatively, and to suppress all warning messages during a connect, use the following syntax...
Connect-VIServer -Server hostname -WarningAction SilentlyContinue
Useful CmdLets etc
Virtual Centre
Command | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
(Get-View ServiceInstance).Content.About
|
Get vCentre software version etc
} Virtual Machine
ESX Host
Get-VMHost MyESX* | Get-VirtualPortGroup | Sort VLanId | Select VLanId, Name ESXi Logs
Storage
Performance StatisticsThe full wealth of performance metrics that are collated by virtual centre are available via the PowerCLI. This can be very useful for creating custom reports, feeding data into other tools etc. It's important to understand how ESX's generate and manage performance data before going any further...
Once in the Virtual Centre database, the data is then rolled up into Historical Interval tables via SQL jobs...
Note that the following are configurable (to a varying degree) via the VI Client.
To see the available intervals for which metrics are available, use the following command (the results will vary depending on whether you're connected to an ESX or a Virtual Centre). [vSphere PowerCLI] E:\> Get-StatInterval
Name SamplingPeriodSecs StorageTimeSecs Client
---- ------------------ --------------- ------
Past Day 300 86400 VMware.VimAutomation....
Past Week 1800 604800 VMware.VimAutomation....
Past Month 7200 2592000 VMware.VimAutomation....
Past Year 86400 31536000 VMware.VimAutomation....
Available MetricsMetrics are available for all objects you'd expect (and probably more), not limited to the following...
For a full list of what objects have what performance counters, and what those counters mean see http://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.PerformanceManager.html. The easiest way to ascertain what metric is available for what object is to use Get-StatType... [vSphere PowerCLI] E:\> Get-VM "vm-server" | Get-StatType
cpu.usage.average
cpu.usagemhz.average
mem.usage.average
mem.granted.average
mem.active.average
...
[vSphere PowerCLI] E:\> Get-VMHost "esx-server*" | Get-StatType
cpu.usage.average
cpu.usagemhz.average
mem.usage.average
mem.granted.average
mem.active.average
mem.shared.average
mem.zero.average
mem.unreserved.average
...
In order to determine which sampling periods are available, you need to use the [vSphere PowerCLI] E:\> Get-StatInterval
Name SamplingPeriodSecs StorageTimeSecs Client
---- ------------------ --------------- ------
Past Day 300 86400 VMware.VimAutomation....
Past Week 1800 604800 VMware.VimAutomation....
Past Month 7200 2592000 VMware.VimAutomation....
Past Year 86400 31536000 VMware.VimAutomation....
However, just because a metric is available, doesn't make it available in the interval you'd like. In order to find out whether a statistic is available in the interval you'd like, you need to use the [vSphere PowerCLI] E:\> Get-VMHost "esx-server*" | Get-Stat -MaxSamples 5 -IntervalSecs 300
MetricId Timestamp Value Unit
--------- ---------- ----- ----
cpu.usage.average 19/01/2010 09:15:00 7.64 %
cpu.usage.average 19/01/2010 09:10:00 6.5 %
cpu.usage.average 19/01/2010 09:05:00 6.37 %
cpu.usage.average 19/01/2010 09:00:00 6.39 %
cpu.usage.average 19/01/2010 08:55:00 6.35 %
cpu.usagemhz.average 19/01/2010 09:15:00 813 MHz
cpu.usagemhz.average 19/01/2010 09:10:00 692 MHz
cpu.usagemhz.average 19/01/2010 09:05:00 678 MHz
cpu.usagemhz.average 19/01/2010 09:00:00 680 MHz
cpu.usagemhz.average 19/01/2010 08:55:00 676 MHz
mem.usage.average 19/01/2010 09:15:00 24.89 %
mem.usage.average 19/01/2010 09:10:00 24.88 %
mem.usage.average 19/01/2010 09:05:00 24.89 %
mem.usage.average 19/01/2010 09:00:00 24.9 %
mem.usage.average 19/01/2010 08:55:00 24.91 %
disk.usage.average 19/01/2010 09:15:00 156 KBps
disk.usage.average 19/01/2010 09:10:00 124 KBps
disk.usage.average 19/01/2010 09:05:00 131 KBps
disk.usage.average 19/01/2010 09:00:00 126 KBps
disk.usage.average 19/01/2010 08:55:00 136 KBps
net.usage.average 19/01/2010 09:15:00 7 KBps
net.usage.average 19/01/2010 09:10:00 8 KBps
net.usage.average 19/01/2010 09:05:00 7 KBps
net.usage.average 19/01/2010 09:00:00 7 KBps
net.usage.average 19/01/2010 08:55:00 7 KBps
sys.uptime.latest 19/01/2010 09:15:00 5173577 second
sys.uptime.latest 19/01/2010 09:10:00 5173277 second
sys.uptime.latest 19/01/2010 09:05:00 5172977 second
sys.uptime.latest 19/01/2010 09:00:00 5172677 second
sys.uptime.latest 19/01/2010 08:55:00 5172376 second
A couple of final points to note
Script ExtractsVirtual MachineVM's with Host and Cluster List$vms = Get-VM | sort -property Name
foreach ($vm in $vms)
{
$vm | Get-Cluster | Select-Object @{Name="VM"; Expression={$vm.name}},@{Name="Current Host"; Expression={$vm.host}},Name
}
VM's Inventory CSV$start = Get-Date
# Create table for output
# Name DC OS UUID IP Cluster ESX's
$table = New-Object system.Data.DataTable "Results"
$col1 = New-Object system.Data.DataColumn Name,([string])
$col2 = New-Object system.Data.DataColumn DC,([string])
$col3 = New-Object system.Data.DataColumn OS,([string])
$col4 = New-Object system.Data.DataColumn UUID,([string])
$col5 = New-Object system.Data.DataColumn MgmtIP,([string])
$col6 = New-Object system.Data.DataColumn Cluster,([string])
#$col7 = New-Object system.Data.DataColumn ESXs,([string])
$table.columns.add($col1)
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)
$table.columns.add($col5)
$table.columns.add($col6)
#$table.columns.add($col7)
$duration = (New-TimeSpan $start (Get-Date)).TotalSeconds
"Created table after $duration secs"
# Get VMs object
$vms = Get-VM | Sort -property Name
$duration = (New-TimeSpan $start (Get-Date)).TotalSeconds
"Got object list of VM's after $duration secs"
foreach ($vm in $vms)
{
$row = $table.NewRow()
$row.Name = (Get-VM -Name $vm).Name
$row.DC = (Get-Datacenter -VM $vm).Name
$row.OS = (Get-VMGuest -VM $vm).OSFullName
$row.UUID = %{(Get-View $vm.Id).config.uuid}
$row.MgmtIP = [string]::join(" ", ((Get-VMGuest -VM $vm).IPAddress)) # Need to join potential list of IP's
$row.Cluster = (Get-Cluster -VM $vm).Name
$table.Rows.Add($row)
"Added row for $vm"
}
$duration = (New-TimeSpan $start (Get-Date)).TotalSeconds
"Populated table after $duration secs"
$table | Format-Table
$table | Export-Csv -path result.csv
VM's with Snapshots RunningBe aware that there's a bug in the way $OutputFile = "VM-Snapshot.csv"
function Log ($text) {
[int]$duration = (New-TimeSpan $start (Get-Date)).TotalSeconds
Write-Host "$duration secs | $text"
}
function Log-NoNewLine ($text) {
[int]$duration = (New-TimeSpan $start (Get-Date)).TotalSeconds
Write-Host "$duration secs | $text" -nonewline
}
$start = Get-Date
$table = New-Object system.Data.DataTable "Results"
$table.columns.add((New-Object system.Data.DataColumn Name,([string])))
$table.columns.add((New-Object system.Data.DataColumn Folder,([string])))
$table.columns.add((New-Object system.Data.DataColumn Snap,([string])))
$table.columns.add((New-Object system.Data.DataColumn Created,([datetime])))
$table.columns.add((New-Object system.Data.DataColumn Size_MB,([single])))
$table.columns.add((New-Object system.Data.DataColumn PowerState,([string])))
Log("Getting list of VMs to check...")
$VMs = Get-VM | Where {$_.PowerState -eq "PoweredOn"} | Sort -Property Name
Log ("Got list of " + ($VMs.Count) + " VMs to check")
$VMno = 0
$VMtot = $VMs.Count
foreach ($vm in $VMs) {
$VMno = $VMno + 1
Log-NoNewLine "[$VMno/$VMtot] $vm - "
$Snaps = Get-Snapshot -VM $vm
if ($Snaps) {
foreach ($snap in $snaps) {
$row = $table.NewRow()
$row.Name = $vm.Name
$row.Folder = Get-Folder -Id $vm.FolderId
$row.Snap = $snap.Name
$row.Created = $snap.Created
$row.Size_MB = $snap.SizeMB
$row.PowerState = $snap.PowerState
Write-Host $row.Snap "| Created" $row.Created "| Size" $row.Size_MB "MB | " $row.PowerState
}
} else {
Write-Host "No snapshot"
}
}
if ($table.rows.Count) {
Log "Completed, writing out table..."
$table | Format-Table
$table | Export-Csv -path $OutputFile
#Send via email
$smtp = New-Object Net.Mail.SmtpClient -arg "smtpserver"
$msg = New-Object Net.Mail.MailMessage
$attach = New-Object Net.Mail.Attachment($OutputFile)
$msg.From = "from"
$msg.To.Add = "to"
$msg.Subject = "Snapshots found running!!"
$msg.Body = "$table.rows.Count snapshots found to be running, see attachment for further info"
$smtp.Send($msg)
$attach.Dispose()
}
Log "Completed!"
To implement the . .\lib\getsnapshotsize-1.ps1
...then I've just replaced Calculates the effective relative CPU shares of VM's contained within resource pools. Can only handle resource pool depth of 1. function Log ($text) {
[int]$duration = (New-TimeSpan $start (Get-Date)).TotalSeconds
Write-Host "$duration secs | $text"
}
$start = Get-Date
Log "Initialising output tables..."
$tSummary = New-Object system.Data.DataTable "Summary Results"
$tSummary.columns.add((New-Object system.Data.DataColumn Name,([string])))
$tSummary.columns.add((New-Object system.Data.DataColumn CPU_Shares,([int])))
$tSummary.columns.add((New-Object system.Data.DataColumn VMs,([int])))
$tSummary.columns.add((New-Object system.Data.DataColumn Weighted,([single])))
$tDetail = New-Object system.Data.DataTable "Detailed Results"
$tDetail.columns.add((New-Object system.Data.DataColumn Res_Pool,([string])))
$tDetail.columns.add((New-Object system.Data.DataColumn VM,([string])))
$tDetail.columns.add((New-Object system.Data.DataColumn Res_CPU_Shares,([int])))
$tDetail.columns.add((New-Object system.Data.DataColumn VM_CPU_Shares,([int])))
$tDetail.columns.add((New-Object system.Data.DataColumn Eff_VM_Shares,([single])))
Log "Getting resource pools..."
$ResPools = Get-ResourcePool
ForEach ($ResPool in $ResPools) {
$VMs = Get-VM -Location $ResPool -NoRecursion | Where {$_.PowerState -eq "PoweredOn"}
If (!$VMs) { #Skip any resource pools with no VM's
Log ("Skipping " + $ResPool.Name + " (no VMs)")
Continue
}
$rSummary = $tSummary.NewRow()
Log ("Analysing " + $ResPool.Name + "...")
$rSummary.Name = $ResPool.Name
$rSummary.CPU_Shares = $ResPool.NumCpuShares
If (!$VMs.Count) { # If only 1 VM in ResPool we don't get a normal array returned (so !VMs.Count is NULL)
$rSummary.VMs = 1
} else {
$rSummary.VMs = $VMs.Count
}
$rSummary.Weighted = $rSummary.CPU_Shares / $rSummary.VMs
$tSummary.Rows.Add($rSummary)
# Get each VM's shares
$totShares = 0
$VMshares = @{}
ForEach ($vm in $VMs) {
$VMShares[$vm.Name] = ($vm | Get-VMResourceConfiguration).NumCpuShares
$totShares += $VMShares[$vm.Name]
}
# Work out each VM's proportional CPU share
ForEach ($vm in $VMs) {
$rDetail = $tDetail.NewRow()
$rDetail.Res_Pool = $ResPool.Name
$rDetail.VM = $vm.Name
$rDetail.Res_CPU_Shares = $ResPool.NumCpuShares
$rDetail.VM_CPU_Shares = $VMShares[$vm.Name]
If ($ResPool.Name -eq "Resources") {
$rDetail.Eff_VM_Shares = $rDetail.VM_CPU_Shares
} else {
$rDetail.Eff_VM_Shares = $ResPool.NumCpuShares * ($rDetail.VM_CPU_Shares / $totShares)
}
$tDetail.Rows.Add($rDetail)
}
}
Log "Done! Writing results out..."
$tSummary | Export-Csv -path ESX-ResPoolInfo-Summary.csv
$tDetail | Export-Csv -path ESX-ResPoolInfo-Detail.csv
$tSummary
Ping All VM's On ESXUseful sanity check prior to and after network level changes $esxToFind = "MyESX*"
$ESX = get-vmhost $esxToFind
if (!$ESX) {
Write-Host "ERROR: No ESX found with name matching expression $esxToFind" -Background Red -ForegroundColor DarkRed
Exit
}
# Get list of VM's on $ESX
$VMs = $ESX | get-vm | Where {$_.PowerState -eq "PoweredOn"} | Sort -property Name
$objPing = New-Object system.Net.NetworkInformation.Ping
Foreach ($VM in $VMs) {
Write-Host $VM.Name.PadRight(20) -nonewline
# Get guest's primary IP address
$ip = (Get-VMGuest -VM $vm).IPAddress[0]
if (!$ip) {
Write-Host "NULL - Skipping test" -Background DarkYellow -ForegroundColor Yellow
Continue
}
Write-Host $ip.PadRight(17) -nonewline
if ($ip -eq '0.0.0.0') {
Write-Host "Skipping" -Background DarkYellow -ForegroundColor Yellow
Continue
}
[string]$res = $objPing.Send($ip).Status
if ($res.CompareTo("Success")) { # Returns 1 if $res doesn't match "Success" !!
Write-Host $res -BackgroundColor DarkRed -ForegroundColor Red
} else {
Write-Host $res -BackgroundColor DarkGreen -ForegroundColor Green
}
}
ESXESX NIC InfoProvides a list of all vmnic speeds for ESX's connected to vCentre $ESXs = Get-VMHost
Foreach ($esx in $ESXs) {
Write-Host $esx.Name $esx.State
$pNICs = (Get-VMHost -Name "MyESX*" | Get-View).Config.Network.Pnic
$result = @{}
Foreach ($pNIC in $pNICs) {
$result[$pNIC.Device] = $pNIC.LinkSpeed.SpeedMB
}
$result = $result.GetEnumerator() | Sort-Object -Property Name
$result
Write-Host
}
ESX Log TailNote that the below only seems to work for ESX3.5, I gave up trying to get it work for both v3 and v4, its a lot easier to just enable ESXi Tech Support Mode and tail the log Pointless for ESX (you can just use tail from the Service Console on the appropriate log), but a godsend it you're using ESXi (and have got used to tailing ESX logs). $PollInterval = 1000 # msec (default - can be overrided by user)
#Get ESX to poll
$ESXs = Get-VMHost | Sort-Object -Property Name
Write-Host "`nWhich ESX's log do you want to tail?"
$num = 0
foreach ($esx in $ESXs) {
Write-Host "[$num]" $ESXs[$num].Name
$num++
}
$num = 0
$num = Read-Host "? [$num] "
$ESX = Get-VMHost $ESXs[$num]
Write-Host " "
# Get Log keys to look at
$keys = Get-LogType -VMHost $ESX
Write-Host "`nWhich log do you want to tail?"
$num = 0
foreach ($key in $keys) {
Write-Host "[$num]" $keys[$num].Key
$num++
}
$num = 0
$num = Read-Host "? [$num] "
$logKey = $keys[$num].Key
# Set polling interval
$PollInterval = Read-Host "`nWhat polling interval do you want to use (msec)? [$PollInterval]"
Write-Host "`nStarting log tail (press Ctrl+C to escape)...`n"
# First pass of log
$ESXLog = Get-Log $LogKey -VMHost $ESX
#Display last $lines
$LineNo = $ESXLog.LastLineNum - $lines
While ($LineNo -le $ESXLog.LastLineNum) {
Write-Host $ESXlog.Entries[$LineNo]
$LineNo++
}
#Polling loop
While (1) {
Start-Sleep -Milliseconds $PollInterval
try {
$ESXLog = Get-Log $logKey -VMHost $ESX -StartLineNum $LineNo
} catch {
if (Select-String -InputObject $_ -pattern "The 0 argument is less than the minimum allowed range of 1" -Quiet) {
Write-Host "ESX log is rolling over..."
$ESXLog = Get-Log $logKey -VMHost $ESX
} else {
Write-Host "UNEXPECTED ERROR: $_"
Exit
}
}
$ESXlog.Entries
$LineNo = $ESXLog.LastLineNum
}
ESX Discovered Networks HintThis function provides the discovered network hints for the network interface its passed. Bear in mind that its just a hint, for an ESX to be aware of a particular vLAN it needs to see traffic. If there's no traffic it will show nothing. Adapted from the following article on the VMware site blog http://blogs.vmware.com/vipowershell/2010/02/how-to-find-out-what-vlans-your-esx-hosts-can-really-see.html function Get-ObservedIPRange {
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,HelpMessage="Physical NIC from Get-VMHostNetworkAdapter")]
[VMware.VimAutomation.Client20.Host.NIC.PhysicalNicImpl]
$Nic
)
process {
$hostView = Get-VMHost -Id $Nic.VMHostId | Get-View -Property ConfigManager
$ns = Get-View $hostView.ConfigManager.NetworkSystem
$hints = $ns.QueryNetworkHint($Nic.Name)
foreach ($hint in $hints) {
foreach ($subnet in $hint.subnet) {
$observed = New-Object -TypeName PSObject
$observed | Add-Member -MemberType NoteProperty -Name Device -Value $Nic.Name
$observed | Add-Member -MemberType NoteProperty -Name VlanId -Value $subnet.VlanId
$observed | Add-Member -MemberType NoteProperty -Name IPSubnet -Value $subnet.IPSubnet
$observed | Add-Member -MemberType NoteProperty -Name BitRatePerSec -Value $nic.BitRatePerSec
Write-Output $observed
}
}
}
}
# Example use:
$result = Get-VMHost MyESX* | Get-VMHostNetworkAdapter | Where {$_.Name -Match ".*vmnic*"} | Get-ObservedIPRange | Sort-Object -Property Device, VlanId
$result | Export-Csv -path ESX-vLANs-Observed.csv
ESX CDP InfoAdapted from posted by LucD on VMware forum http://communities.vmware.com/message/977487 Get-VMHost | Sort -Property Name | %{Get-View $_.ID} | %{$esxname = $_.Name; Get-View $_.ConfigManager.NetworkSystem} | %{
foreach($physnic in $_.NetworkInfo.Pnic){
$pnicInfo = $_.QueryNetworkHint($physnic.Device)
foreach($hint in $pnicInfo){
Write-Host $esxname $physnic.Device $hint.connectedSwitchPort.DevId $hint.connectedSwitchPort.PortId
}
}
}
ESX BIOS, NIC and HBA Driver VersionsGetting BIOS, NIC and HBA driver versions is dependant on what's reported to the ESX by the hardware vendor's CIM provider. This script was written using various HP servers and may well only work for them. # ===============================================================
# ESX Inventory Getter
# ===============================================================
# Simon Strutt November 2010
# ===============================================================
#
# Version 1
# - Initial Creation
#
# Version 2 - Dec 2010
# - Added BiosVer, CpuCores, CpuModel, HbaModel, NicModel
# - Bugfix: Corrected VC connection handling
# - Removed dependancy on obseleted properties (from upgrade to PowerCLI v4.1.1)
#
# Limitations
# - Tested on HP DL380 (G7), BL465 (G7), BL495 (G6), BL685 (G5)
# - Supports 1 distinct HBA model, 2 distinct NIC models
#
# ================================================================
$start = Get-Date
$OutputFile = "ESXs.csv"
$VC_List = "ESX-Check.csv"
$UserFile = "User.fil"
$PassFile = "Pass.fil" # Encrypted file to store password in
$Results = @()
# Include library files
. .\lib\Standard.ps1
Start-Transcript -Path ESX-Inventory.log
Log "Started script run at $start"
# Function-U-like ===================================================================================
Function Get-NicDriverVersion ($view, $driver) {
# Gets the CIM provided driver version (tallies up driver name with CIM software component)
$result = ($view.Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo | Where {$_.Name -like $driver + " driver*"} | Get-Unique).Name
$result = ([regex]::Matches($result, "(\b\d)(.*)(?=\s)"))[0].Value # HP regex to extract "2.102.440.0" for example
$result
}
# ===================================================================================================
# Load list of VC's
try {
$VCs = Import-CSV $VC_List
} catch {
Log "ERROR: Failed to load list of vC's"
Exit
}
# Load password credential from encrypted file
$pass = Get-Content $PassFile | ConvertTo-SecureString
$user = Get-Content $UserFile
$cred = New-Object System.Management.Automation.PsCredential($user, $pass)
foreach ($vc in $VCs) {
# Connect to VC
try {
Log("Connecting to " + $vc.vc)
$VCconn = Connect-VIServer -Server $vc.vc -Credential $cred -errorAction "Stop"
} catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidLogin] {
Log("Unable to connect to vCentre, invalid logon error !!")
Log("Abandoning further script processing in order to prevent potential account lockout.")
Break
} catch {
Log("Unable to connect to vCentre - " + $_)
Continue
}
# Get ESX objects
Log("Getting list of ESXs to check on " + ($vc.vc) + "...")
$ESXs = Get-VMHost -Server $vc.vc | Sort -property Name
Log("Got list of " + ($ESXs.Count) + " ESXs to check")
ForEach ($ESX in $ESXs) {
$row = "" | Select VC, Cluster, Name, IP, Version, Make, Model, BiosVer, CpuCores, CpuModel, HbaModel, HbaDriver, HbaDriverVer, Nic1Model, Nic1Driver, Nic1DriverVer, Nic2Model, Nic2Driver, Nic2DriverVer
Log($ESX.Name)
# Store objects which get re-used for efficiency
$ESXview = Get-View -VIObject $ESX
$VMHostNetworkAdapter = Get-VMHostNetworkAdapter -VMHost $esx
# Get the basics
$row.VC = $vc.vc
$row.Cluster = $ESX.Parent
$row.Name = $ESX.Name.Split(".")[0]
$row.IP = ($VMHostNetworkAdapter | Where {$_.ManagementTrafficEnabled -eq "True" -or $_.DeviceName -like "vswif*" -or $_.Name -eq "vmk0"}).IP
$row.Version = $ESX.Version + " (" + $ESX.Build + ")"
$row.Make = $ESX.Manufacturer
$row.Model = $ESX.Model
# Now onto the more ESX version / hardware specfic stuff (new versions of hardware will require further work below)
# BIOS
if ($ESXView.Hardware.BiosInfo) { # Works on some systems
$row.BiosVer = $ESXview.Hardware.BiosInfo.BiosVersion + " " + $ESXview.Hardware.BiosInfo.ReleaseDate.ToString("yyyy-MM-dd") # Need date for HP servers as they use same version no for diff versions!
} else {
$row.BiosVer = ($ESXview.Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo | Where {$_.Name -like "*BIOS*"}).Name
$row.BiosVer = ([regex]::Matches($row.BiosVer, "[A-Z]\d{2} 20\d{2}-\d{2}-\d{2}"))[0].Value # HP regex to extract "A19 2010-09-30" for example
}
# CPU info
$row.CpuCores = $ESXview.Hardware.CpuInfo.NumCpuCores.ToString() + " (" + $ESXview.Hardware.CpuPkg.count + "x" + $ESXview.Hardware.CpuPkg[0].ThreadId.count + ")"
$row.CpuModel = $ESX.ExtensionData.Summary.Hardware.CpuModel
# HBA info (script assumes only one HBA model in use)
$row.HbaModel = ($ESX.ExtensionData.Config.StorageDevice.HostBusAdapter | Where {$_.Key -like "*FibreChannel*"})[0].Model
$row.HbaDriver = ($ESX.ExtensionData.Config.StorageDevice.HostBusAdapter | Where {$_.Key -like "*FibreChannel*"})[0].Driver # Includes version for ESX3
$row.HbaDriverVer = ($ESXview.Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo | Where {$_.Name -like "*" + $row.HbaDriver + "*"}).Name
$row.HbaDriverVer = ([regex]::Matches($row.HbaDriverVer, "(\b\d)(.*?)(?=\s)"))[0].Value
# NIC info (script only supports two distinct NIC types)
$nics = $ESXview.Hardware.PciDevice | Where {$_.ClassId -eq 512} | Select VendorName, DeviceName, Id | Sort-Object -Property DeviceName -Unique
if ($nics.count) {
$row.Nic1Model = $nics[0].VendorName + " " + $nics[0].Devicename
$row.Nic2Model = $nics[1].VendorName + " " + $nics[1].Devicename
# Use PCI ID to match up NIC hardware type with driver name
$row.Nic1Driver = ($VMHostNetworkAdapter | Where {$_.ExtensionData.Pci -eq $nics[0].Id}).ExtensionData.Driver
$row.Nic2Driver = ($VMHostNetworkAdapter | Where {$_.ExtensionData.Pci -eq $nics[1].Id}).ExtensionData.Driver
$row.Nic1DriverVer = Get-NicDriverVersion $ESXview $row.Nic1Driver
$row.Nic2DriverVer = Get-NicDriverVersion $ESXview $row.Nic2Driver
} else {
# Annoyingly, $nics is not an array if there's only one NIC type, hence seperate handling
$row.Nic1Model = $nics.VendorName + " " + $nics.Devicename
$row.Nic1Driver = ($VMHostNetworkAdapter | Where {$_.ExtensionData.Pci -eq $nics.Id}).ExtensionData.Driver
$row.Nic1DriverVer = Get-NicDriverVersion $ESXview $row.Nic1Driver
}
$Results = $Results + $row
}
Disconnect-VIServer -Server $VCconn -Confirm:$false
}
$Results | Format-Table *
Log("Writing results to $OutputFile...")
$Results | Export-Csv -path $OutputFile -NoTypeInformation
Log("All completed")
Stop-Transcript
Daily ESX Up/Down CheckSimple script that runs every morning just before I start work to provide a basic sanity check email to glance through over the first tea of the day. #############################################################################################
#
# ESX Checker
#
#############################################################################################
#
# By Simon Strutt
#
# Version 1 - Aug 10
# - Initial creation
#
# Version 2 - Oct 10
# - Fixed VIServer handling (was never good but stuffed by upgrade to PowerCLI 4.1)
# - Improved email in case of vCentre connection failure
#
# Version 2.1 - Nov 10
# - Minor text changes
#
# Version 2.2 - Nov 10
# - Added InvalidLogon catch to vCentre logon to prevent a/c lockout
#
# Version 3 - Dec 10
# - Truncated displayed ESX hostname (stripped domain)
# - Added "since <datetime>" info to ESX's that aren't in a good state
# - Bugfix: Incorrect email config used when VC list config file load failed
#
# Version 3.1 Jan 11
# - Workaround: Powershell/MS DateTime object returns incorrect (US) date format
#
# Version 3.2 Jan 11
# - Bugfix: Extraneous " hrs" on end of v3.1 fixed datetimes
#
#############################################################################################
$DateFormat = "%R hrs, %a %d %b %Y"
$EmailBody = "Results of ESX status check as of " + (get-date -uFormat "$DateFormat.`n`n")
$EmailRecipients = "person1@domain.com,person2@domain.com"
$EmailSender = "VI-Mgmt@domain.com"
$smtp = New-Object Net.Mail.SmtpClient -arg "smtp-server.domain.com"
$VCAlertToSend = 0
$ESXAlertToSend = 0
$start = Get-Date
try {
$ToCheck = Import-CSV "ESX-Check.csv"
} catch {
$EmailBody = "ERROR: Failed to load config file, no checks have been done.`n"
$EmailBody += $_
$smtp.Send($EmailSender, $EmailRecipients, "ESX-Check: Check failed", $EmailBody)
Exit
}
$pass = Get-Content "Cred.fil" | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PsCredential("MPADGLOBAL\simonstrutt",$pass)
foreach ($vc in $ToCheck) {
Write-Host "Checking ESXs on" $vc.vc
$VChasBadESX = 0
try {
$VCconn = Connect-VIServer -Server $vc.vc -Credential $cred -NotDefault -errorAction "Stop"
} catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidLogin] {
$EmailBody += ($vc.vc).ToUpper() + "`t" + "Unable to connect to vCentre, invalid logon error !!`n`n"
$EmailBody += "Abandoning further script processing in order to prevent potential account lockout.`n"
$VCAlertToSend = 1
Break
} catch {
$EmailBody += ($vc.vc).ToUpper() + "`t" + "Unable to connect to vCentre, ESX's not checked !!`n"
$VCAlertToSend = 1
Continue
}
$ESXs = Get-VMHost -Server $vc.vc | Where {$_.State -ne "Connected"} | Sort -property Name
if ($ESXs) {
foreach ($ESX in $ESXs) {
# Attempt to work out when ESX went into its bad state
Switch ($ESX.State) {
NotResponding {
$events = Get-VIEvent -Entity $ESX -Types Error | Where {$_.fullFormattedMessage -like "*is not responding*"}
}
Maintenance {
$events = Get-VIEvent -Entity $ESX -MaxSamples 1000 | Where {$_.fullFormattedMessage -like "*has entered maintenance mode*"}
}
Disconnected {
$events = Get-VIEvent -Entity $ESX -MaxSamples 1000 | Where {$_.fullFormattedMessage -like "*Disconnected from*"}
}
Default {
$events = 0
}
}
If ($events) {
$SinceString = " since " + (Get-Date $events[0].CreatedTime -uFormat $DateFormat)
} else {
$SinceString = ""
}
$EmailBody += $vc.vc + "`t" + ($ESX.Name.Split(".")[0]).ToUpper() + "`t" + $ESX.State + $SinceString + "`n"
$ESXAlertToSend = 1
$VChasBadESX = 1
}
}
if (!$VChasBadESX) {
$EmailBody += $vc.vc + "`t" + "All good`n"
}
Disconnect-VIServer -Server $VCconn -Confirm:$false
}
$end = Get-Date
$EmailBody += "`n`n`nCheck started : " + (Get-Date $start -uFormat $DateFormat) + "`n"
$EmailBody += "Check finished : " + (Get-Date $end -uFormat $DateFormat) + "`n"
$EmailBody += "Generated by script: " + ($MyInvocation.MyCommand.Name) + "`n"
$EmailBody += "Sent from machine : $env:computername"
$EmailBody
Write-Host "Sending email..."
if ($VCAlertToSend) {
$smtp.Send($EmailSender, $EmailRecipients, "ESX-Check: Unable to connect to some/all vCentres", $EmailBody)
} elseif ($ESXAlertToSend) {
$smtp.Send($EmailSender, $EmailRecipients, "ESX-Check: Some ESX's are NOT CONNECTED", $EmailBody)
} else {
$smtp.Send($EmailSender, $EmailRecipients, "ESX-Check: All connected OK", $EmailBody)
}
if (-not $?) {
Write-Host "SMTP send failed!!"
Start-Sleep(30000)
}
StorageVM's with Datastores ListList of Virtual Machines, and their datastores (with usage) $datastoreExp = @{N="Datastore"; E={ ($_ | get-datastore | select-object -first 1).Name }}
$diskSizeExp = @{N="Total Disk"; E={ ($_ | get-harddisk | measure-object -property CapacityKB -sum).Sum }}
get-vm | select Name, $datastoreExp, $diskSizeExp | sort -property Datastore,"Total Disk"
VM Storage UsageTotal storage usage (including any snapshots, logs, etc, etc), not just the VMDK files. Adapted from post by Arnim van Lieshout http://www.van-lieshout.com/2009/07/how-big-is-my-vm/ function Get-VMDiskUsage($vm2do)
{
#Initialize variables
$VMDirs =@()
$VMSize = 0
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = New-Object VMware.Vim.FileQueryFlags
$searchSpec.details.fileSize = $TRUE
Get-View -VIObject $vm2do | % {
#Create an array with the vm's directories
$VMDirs += $_.Config.Files.VmPathName.split("/")[0]
$VMDirs += $_.Config.Files.SnapshotDirectory.split("/")[0]
$VMDirs += $_.Config.Files.SuspendDirectory.split("/")[0]
$VMDirs += $_.Config.Files.LogDirectory.split("/")[0]
#Add directories of the vm's virtual disk files
foreach ($disk in $_.Layout.Disk) {
foreach ($diskfile in $disk.diskfile){
$VMDirs += $diskfile.split("/")[0]
}
}
#Only take unique array items
$VMDirs = $VMDirs | Sort | Get-Unique
foreach ($dir in $VMDirs){
$ds = Get-Datastore ($dir.split("[")[1]).split("]")[0]
$dsb = Get-View (($ds | get-view).Browser)
$taskMoRef = $dsb.SearchDatastoreSubFolders_Task($dir,$searchSpec)
$task = Get-View $taskMoRef
while($task.Info.State -eq "running" -or $task.Info.State -eq "queued"){$task = Get-View $taskMoRef }
foreach ($result in $task.Info.Result){
foreach ($file in $result.File){
$VMSize += $file.FileSize
}
}
}
}
# VM disk usage in GB
$VMSize/1048576000
}
Get-VMDiskUsage (Get-VM "MyVM")
ESX Storage EventsCreates a CSV of storage events (eg path up/downs). Useful in order to be analyse storage events, for example if you need to tally up to SAN logs etc. Note the $OutputFile = "ESX-EventsStorage.csv"
$Results = @()
$events = Get-VIEvent -MaxSamples 1000 | Where {$_.EventTypeID -like "esx.*.storage.*"}
foreach ($event in $events) {
$row = "" | Select Date, Host, EventID, Device, Datastore, Path
$row.Date = $event.CreatedTime
$row.Host = $event.Host.Name.Split(".")[0]
$row.EventID = $event.EventTypeID
# Allow for Argument Keys being mixed up between "problem" aka fault start and "clear" aka fault end events
$row.Device = $event.Arguments[0].Value
if ($row.EventID -like "esx.problem.*") {
$row.Datastore = $event.Arguments[2].Value
$row.Path = $event.Arguments[1].Value
} elseif ($row.EventID -like "esx.clear.*") {
$row.Datastore = $event.Arguments[1].Value
$row.Path = $event.Arguments[2].Value
} else {
Write-Host "ERROR: Unexpected EventTypeID - " $row.EventID
}
$Results = $Results + $row
}
$Results | Format-Table *
$Results | Export-Csv -path $OutputFile -NoTypeInformation
ESX Datastore to LUN Mapping (ESX3)Its bizarrely difficult to be able to map VMware presented datastore names to the underlying LUN's, despite the fact that its readily available via the VI Client. The following was adapted from the work of Catman found in this forum thread - http://communities.vmware.com/thread/240466#240466. As I expect to maybe want some of the other fields available during the working my notes from working through this are to be found here # Thieved and adapted from the good work by catman...
# http://communities.vmware.com/thread/240466#240466
$objESX = Get-VMHost "My_ESX*"
# Get .NET views for host and storage system
$objViewESX = Get-View -id $objESX.id
$objViewESXstorageSys = Get-View -id $objViewESX.ConfigManager.StorageSystem
# Get FC HBAs
$HBAs = $objViewESXstorageSys.StorageDeviceInfo.HostBusAdapter | Where-Object {$_.Key -like "*FibreChannelHba*"}
foreach ($hba in $HBAs) {
# Enumerate LUNs
$LUNcount = $objViewESXstorageSys.StorageDeviceInfo.MultiPathInfo.Lun.Length
for ($LUNidx = 0; $LUNidx -lt $LUNcount; $LUNidx++ ) {
$objScsiLUN = $objViewESXstorageSys.StorageDeviceInfo.MultiPathInfo.Lun[$LUNidx]
# Enumerate paths on LUN
$PathCount = $objScsiLUN.Path.Length
for ($PathIdx = 0; $PathIdx -lt $PathCount; $PathIdx++) {
$objSCSIpath = $objViewESXstorageSys.StorageDeviceInfo.MultiPathInfo.Lun[$LUNidx].Path[$PathIdx]
# Only care about one path, active on current HBA
if (($objSCSIpath.PathState -eq "active") -and ($objSCSIpath.Adapter -eq $hba.Key)) {
# Now get the disk that we want
$objSCSIdisk = $objViewESXstorageSys.StorageDeviceInfo.ScsiLun | Where-Object{ ($_.CanonicalName -eq $objScsiLUN.Id) -and ($_.DeviceType -eq "disk") }
# Now get the datastore info for disk
$MountCount = $objViewESXstorageSys.FileSystemVolumeInfo.MountInfo.Length
for ($MountIdx = 0; $MountIdx -lt $MountCount; $MountIdx++ ) {
if ($objViewESXstorageSys.FileSystemVolumeInfo.MountInfo[$MountIdx].Volume.Type -eq "VMFS" ) {
$objVolume = $objViewESXstorageSys.FileSystemVolumeInfo.MountInfo[$MountIdx].Volume
$ExtentCount = $objVolume.Extent.Length
for ($ExtentIdx = 0; $ExtentIdx -lt $ExtentCount; $ExtentIdx++ ) {
$objExtent = $objVolume.Extent[$ExtentIdx]
# Match extent name to disk name
if ($objExtent.DiskName -eq $objSCSIdisk.CanonicalName) {
Write-Host($objSCSIdisk.Vendor + " " + $objSCSIdisk.Model + " " + $objSCSIdisk.CanonicalName + "`t" + $objVolume.Name)
}
}
}
}
}
}
}
}
Gives an output a bit like this... HP HSV200 vmhba1:0:20 VMFS-DS-08 HP HSV200 vmhba1:0:7 VMFS-DS-01 HP HSV200 vmhba1:0:22 VMFS-DS-10 HP HSV200 vmhba1:0:8 VMFS-DS-02 HP HSV200 vmhba1:0:10 VMFS-DS-03 HP HSV200 vmhba1:0:18 VMFS-DS-07 HP HSV200 vmhba1:0:21 VMFS-DS-09 HP HSV200 vmhba1:0:12 VMFS-DS-05 HP HSV200 vmhba1:0:11 VMFS-DS-04 HP HSV200 vmhba1:0:3 VMFS-DS-Templates HP HSV200 vmhba1:0:2 VMFS-DS-ISOs HP HSV200 vmhba1:0:30 VMFS-DS-SCRATCH HP HSV200 vmhba1:0:13 VMFS-DS-06
ESX Datastore to LUN Mapping (ESX4)Produces a similar output to the above, but works for ESX4 servers. function Get-DS-LUNs-v4 ($objESXs) {
foreach ($cluster in (Get-View -ViewType "ClusterComputeResource")) {
$vmhostsview = $cluster.host | % { Get-View $_ }
$vmhostview = $vmhostsview | Select -first 1
$ScsiLuns = $vmhostsview | % { $_.Config.StorageDevice.ScsiLun } | Select -unique *
$UUIDs = $ScsiLuns | Select -unique UUID
$Datastores = $vmhostsview | % { $_.Config.FileSystemVolume.MountInfo } | % { $_.Volume } | Select -Unique *
$HostLUN = $vmhostsview | % { $_.Config.StorageDevice.ScsiTopology.Adapter } | % { $_.Target | % { $_.LUN } } | Select -unique *
foreach ($UUID in $UUIDs) {
$Lun = $ScsiLuns | ? { $_.UUID -eq $UUID.UUID } | Select -first 1
$objVolume = "" | Select Datastore, Make, Model, LUN
$objVolume.LUN = ($HostLUN | ? { $_.ScsiLun -eq $Lun.Key } | select -unique LUN).LUN
$objVolume.Make = $Lun.Vendor
$objVolume.Model = $Lun.Model
foreach ($vol in $Datastores) {
if ($vol.extent | % { $_.diskname -eq $Lun.CanonicalName}) {
$objVolume.Datastore = $vol.Name
}
}
$objVolume
}
}
}
|