Get-VMHostPnicCDP for check CDP/LLDP information for ESXi hostsystem physical nic.

Today i had to check some data in CDP, hence i wrote this script. I think it’s well documented, so it does not need description from my side. One thing to note, is that i have not checked if it prints out properly lldp information if you are using -lldp switch, i just assumed it’s going to be ok, looking at the vsphere documentation for this lldpinfo object.

		Gets CDP/LLDP information for ESXi physical nic.

		Gets CDP/LLDP information for ESXi physical nic.

		VMHost Can be piped to this function, it can VMHost object from get-vmhost or it get be HostSystme object from Get-View.

		This is the physical nic adapter name, for example: vmnic0 , it supports array as well: vmnic0,vmnic2,vmnicN.

		If paramenter -lldp was give, it will output content of lldpinfo from QueryNetworkHint method.

		PS C:\> Get-VMHostPnicCDP   -pnic vmnic0 -VMHost 'esxi1.local.lan'
		Without passing through pipeline it acceppts 1 vmhost only.

		PS C:\> Get-VMHostPnicCDP  -VMHost 'esxi1.local.lan'
		When -pnic parameter is skipped, CDP will be retrieved for all pnics in ESXi.

		PS C:\> 'esxi1.local.lan','esxi2' | Get-VMHostPnicCDP   -pnic vmnic0
		You can pass multiple VMHost names through pipeline

		PS C:\> get-vmhost -name 'esxi1.local.lan' | Get-VMHostPnicCDP -pnic vmnic1
		You can pass object directly from get-vmhost

		PS C:\>  get-view -viewtype hostsystem -filter @{'name'='esxi1.local.lan'} | Get-VMHostPnicCDP -pnic vmnic1
		You can pass object directly from get-view

		NAME: Get-VMHostPnicCDP
		AUTHOR: Grzegorz Kulikowski
		LASTEDIT : 09/07/2015


function Get-VMHostPnicCDP {
		[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
	begin {
			if (!$global:defaultVIserver) { 'Not connected to VirtualCenter';break }
	process {
		try {
			switch (($VMHost.GetType()).FullName)
					$NetworkSystemConfigManager = get-view -id $vmhost.ExtensionData.ConfigManager.NetworkSystem
					$NetworkSystemConfigManager = Get-View -id $vmhost.ConfigManager.NetworkSystem
					$VMHost = Get-View -viewtype HostSystem -Property Name, ConfigManager.NetworkSystem -Filter @{ 'name' = $VMHost }
					$NetworkSystemConfigManager = Get-View -id $vmhost.ConfigManager.NetworkSystem
			if (!$pnic) { [string[]]$pnic = $NetworkSystemConfigManager.NetworkConfig.Pnic | %{ $_.device } }
			foreach ($EsxiPnic in $pnic)
				if ($lldp) { $NetworkSystemConfigManager.QueryNetworkHint($esxipnic) | %{ $_.lldpinfo } | Select-Object *, @{ n = 'Esxi'; e = { $ } }, @{ n = 'ESXiPnic'; e = { $EsxiPnic } } }
					$NetworkSystemConfigManager.QueryNetworkHint($esxipnic) | %{ $_.ConnectedSwitchPort } | Select-Object *, @{ n = 'Esxi'; e = { $ } }, @{ n = 'ESXiPnic'; e = { $EsxiPnic } }
		catch [VMware.Vim.VimException]
			'Maybe the pnic you are reffering to, does not exist ?'
	end {
		try {
		catch {

Copy n paste, and enjoy 😉


How to get a list of hard disks in ESXi host system ?

That’s the only way so far i have figured out how to do it. Getting the information through CIM.

$esxi = 'myhost'
$CIMOpt = New-CimSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -Encoding Utf8 –UseSsl
$Session = New-CimSession -Authentication Basic -Credential $cred -ComputerName $esxi -port 443 -SessionOption $CIMOpt
Get-CimInstance -CimSession $Session -Namespace 'root/cimv2' -ClassName 'CIM_StorageExtent' | ?{$_.CreationClassName -eq 'HPVC_SAStorageExtent'} | Select PSComputerName, Caption, ElementName

In return i get this:

PSComputerName Caption ElementName
-------------- ------- -----------
esxi01.local.lan Disk 1 on HPSA1 Disk 1 on HPSA1 : Port 1I Box 1 Bay 1 : 136GB : Data Disk
esxi02.local.lan Disk 2 on HPSA1 Disk 2 on HPSA1 : Port 1I Box 1 Bay 2 : 136GB : Data Disk

This will work only for HP in this version as i am filtering output with HPVC_SAStorageExtent
Well, better this than nothing 😉

Get-gEvents for getting vsphere events with some additional filtering

Some time ago i wrote a dirty function that utilizes eventfilterspec to filter out events. I knew it had one flaw, because as far as i remember i was using there QueryEvents method. Nothing bad with that, but as far as i understand it will only return up to 1000 records. So i have finally modified it so it uses EventCollector. It has support for searching by eventchainID, eventTypeId, username, date. I have tried to document function as much as possible. For me it works ok. There is some tiny support for checking eventTypeIds inside the function. If something is not clear make sure you will read the help
get-help get-gEvents -full
One confusing bit might be that while passing the entityname , you have to pass the viewtype as well. Well i did it because i use it this way 😉 So i use Get-View -ViewType [type] -Filter @{‘name’=XXX’}
So it’s up to you what exactly you are looking for, while specifying the entity, In filter name will be put, but ViewType specifies the type of object, whether it will be a datacenter, vm , cluster, datastore etc..
I tried to implement the switch for enabling/disabling full formatted message, but for some reason it was not working, no idea why.

Quick examples:

How to check how many vmotions DRS has performed inside a particular cluster ?

(Get-gEvents -StartDate (get-date).AddDays(-1) -types 'VmBeingHotMigratedEvent' -EntityName 'Cluster_name' -ViewType ClusterComputeResource).Count

How to check how many vmotions DRS has performed inside a particular datacenter ?

(Get-gEvents -StartDate (get-date).AddDays(-1) -types 'VmBeingHotMigratedEvent' -EntityName 'Datacenter_name' -ViewType Datacenter).count

Endless possibilities basically.. i will throw 1 more
How many times user X has powered off a VM in last 7 days

(Get-gEvents -StartDate (get-date).AddDays(-7) -EventTypeIds 'VmPoweredOffEvent' -EventUsername 'DOMAIN\UserX' -systemUser:$false ).count

How many were there vmotions in some cluster during last 7 days

Get-gEvents -StartDate (get-date).AddDays(-7) -EventTypeIds 'VmFailedMigrateEvent' -EntityName 'SomeCluster' -ViewType ClusterComputeResource}

What if you want to check for other dates ? Use: -StartDate with (get-date).AddDays(-7) for example, to go 7 days back. Same goes for end date, -EndDate (get-date…..

When you will be giving parameter for the Entity, you can still use the -Recursion parameter with it to control whether you want to inspect only THAT entity, or its children, or both.

If you will skip -startdate or -enddate make sure you know that function is setting those variable up anyway with default values of: Enddate -> NOW , -startdate 7 days ago.

Make sure you know that by default if you will skip those parameters : systemuser and recursion will be set to true and all . So if you wonder how come you received system events if you were looking for user’s events, mark the -systemuser:$false , if you want to look only for event on entity and do not want to step inside its children use -recursion ‘self’ , because by default i set it to ‘all’.

You do not need to provide entity for this function to start, if not given it searches through everything.


function Get-gEvents
  <# .SYNOPSIS Get-gEvents is utilizing EventCollector to get information about events. .DESCRIPTION Get-gEvents utilizies most of what EventCollector has to offer. If Start or End date is not specified, the default values of: End - Now, Start - last 7 days will be applied. If you are using EventUserName parameter, you can then choose SystemUser parameter. By default it is selected as True, so if you do not want to see system events please set it to -SystemUser:$false , so you will get only user's records. This function also allows to get current list of types for events, if you do not remember a particular event, first try this function with -ListMainEVTypes From there you can use -ListSecEVTypes with -SubEVType and give it value of name of typse from previous command. .EXAMPLE Count DRS vmotions in Virtual Center for last day. (Get-gEvents -StartDate (get-date).AddDays(-1) -EventTypeIds 'VmBeingHotMigratedEvent').count .EXAMPLE Get-gEvents -ListSecEVTypes -SubEVType 'VmEvent' .PARAMETER ListMainEVTypes If used , you will received main event types categories. .PARAMETER ListSecEVTypes If used, you have to use SuvEVType to select the right subcategory to expand. .PARAMETER SubEVType From output of -ListMainEVTypes you can paste its name to -SubEVType , in response you will get more detailed event types. for example -SubEVType 'VmEvent' .PARAMETER eventChainId If you are interested only in particular event chain you can specify it's id here. .PARAMETER EventUsername You can search only user's events by giving in this parameter the login of user for example: 'userX', 'DOMAIN\UserX' .PARAMETER EntityName Name of the entity for which or underwhich you will be looking for events. It works together with ViewType. .PARAMETER systemUser In use with EventUsername, By default systemUser is $True, so that you will see the user's and the system events. If you will give -systemUser:$false then, only user's events will be gathered. .PARAMETER EventTypeIds This parameters can handle multiple ids of eventtypes separated by comas, for example : VmBeingHotMigratedEvent for checking drs migrations or VmPoweredOffEvent for vms getting powered off. If you do not know the specific type id, you can always run this function with ListMainEVTypes and ListSecEVTypes paramegters to get hints. .PARAMETER category Possible categories are: info, warning, error, user. .PARAMETER StartDate Start date for the search query. For example (get-date) for now, or (get-date).AddDays(-1) for yesterday. .PARAMETER EndDate End date for the search query. For example (get-date) for now, or (get-date).AddDays(-1) for yesterday. .PARAMETER Recursion You can choose from Self,Children,All options. Self is only checking events on Entity itself, Childred only on Children entities, and all is Self+Children. .PARAMETER ViewType This is the ViewType that you would normally use within Get-View to find that particular entity. For example HostSystem for host, or VirtualMachine for VM, or ClusterComputeResource for Cluster. .LINK .NOTES Author : Grzegorz Kulikowski #>
	[cmdletbinding(DefaultParametersetName = "Main Usage")]
	param (
		[parameter(Mandatory = $true, ParameterSetName = "Main Events Types Listing")][switch]$ListMainEVTypes,
		[parameter(Mandatory = $true, ParameterSetName = "Detailed SubEvents Types Listing")][switch]$ListSecEVTypes,
		[parameter(Mandatory = $true, ParameterSetName = "Detailed SubEvents Types Listing")]$SubEVType,
		[parameter(Mandatory = $true, ParameterSetName = "ChainID Events Listing")][string]$eventChainId,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][string[]]$EventUsername,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][bool]$systemUser = $true,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][string]$EntityName,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][String[]]$EventTypeIds,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][VMware.Vim.EventCategory]$category,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][datetime]$StartDate = (Get-Date).AddDays(-7),
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][datetime]$EndDate = (Get-date),
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][vmware.vim.EventFilterSpecRecursionOption]$Recursion = 'all',
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][ValidateSet('ClusterComputeResource', 'ComputeResource', 'Datacenter', 'Datastore', 'DistributedVirtualPortgroup', 'DistributedVirtualSwitch', 'Folder', 'HostSystem', 'Network', 'OpaqueNetwork', 'ResourcePool', 'StoragePod', 'VirtualApp', 'VirtualMachine', 'VmwareDistributedVirtualSwitch')][string]$ViewType
	switch ($PsCmdlet.ParameterSetName)
		"Main Events Types Listing"  {
			[VMware.Vim.VmEvent].Assembly.GetTypes() | ? { $_.BaseType -eq [VMware.Vim.Event] }
		"Detailed SubEvents Types Listing"  {
			[VMware.Vim.VmEvent].Assembly.GetTypes() | ? { $_.BaseType -like "VMware.Vim.$SubEVType" }
		"Main Usage" {
			$si = get-view -id ServiceInstance
			$em = get-view -id $si.Content.EventManager
			$EventFilterSpec = New-Object VMware.Vim.EventFilterSpec
			$EventFilterSpec.eventTypeId = $EventTypeIds
			if ($StartDate -or $EndDate)
				Write-debug "Date was given"
				$EventFilterSpec.Time = New-Object Vmware.Vim.EventFilterSpecByTime
				$EventFilterSpec.Time.beginTime = $StartDate
				$EventFilterSpec.Time.endTime = $EndDate
			if ($category)
				Write-debug "Category was given"
				$EventFilterSpec.Category = $category
			if ($EventUsername)
				Write-debug "User was given"
				$EventFilterSpec.userName = New-Object Vmware.Vim.EventFilterSpecByUsername
				$EventFilterSpec.userName.systemUser = $systemUser
				$EventFilterSpec.userName.userList = $EventUsername
			if ($EventChainId)
				Write-debug "EventChainId was given"
				$EventFilterSpec.EventChainId = $EventChainId
			if ($EntityName)
				Write-debug "Entity was given"
				$entity = get-view -viewtype $ViewType -Filter @{ 'name' = $EntityName } -Property name
				$EventFilterSpec.Entity = New-Object VMware.Vim.EventFilterSpecByEntity
				$EventFilterSpec.Entity.Entity = $entity.moref
				$EventFilterSpec.Entity.Recursion = $Recursion
			$EventCollector = get-view -id $EM.CreateCollectorForEvents($Eventfilterspec)
			write-debug "Collector rewinded"
			$events = $null
			while ($EventInWindow = $EventCollector.ReadNextEvents(100))
				$Events += $EventInWindow
				write-debug "Reading next window"

vLanIdentificator – for checking subnets for Virtual Machines and their vlan info.

I have created this tiny program that will help me sometimes look for other vms that are supposed to sit inside some particular vlan/subnet. The idea is really simple. You are given an ip and somebody is asking you which vlan is that , for example. If you don’t have a hobby of memorizing vlans in your networks might try to use this program. If given a particular IP, and netmask, it will scan all ips within it and check if you have Virtual Machines in Virtual Center that are utilizing ips from that range. In addition it will tell you where the VM is being hosted and which portgroup on esxi hostsystem it is utilizing and of course its vlan id. I hope some of you may find it useful. Executable is prepared for powershell version 3. In general i guess it’s compatible only with >PowerShell 3 , since how i wrote it. I have not tested it yet with Distributed Switches. I have not tested it on other version than vsphere 5.0.If you test it on different version , let me know in the comment. I will make sure it runs ok with vDS as well soon. VMware tools need to be installed in VM in order to report the IP back. This tiny program basically just runs a loop for ips utilizing SearchIndex and its FindAllByIp function. There isn’t much magic happening here 😉 In order to write it i used Get-IPrange by Barry Chum – IP regex – On the computer on which you want to run it , you will need to have PowerCLI installed. When you are asked for credentials, read-only permission will be enough to retrieve information. Screenshot of how it looks is below. vlanidentificator Executable can be found HERE PSF (Powershell Studio) source code can be found HERE No this is not finished 😉 I am still learning windows forms, so that’s why it looks like it looks. Most important for now , is that it does its job. –Update 21/07/2015 Added support for VMware Distributed Switches Fixed minor bugs

vSphere 5.0 permissions issue / bug ?

vSphere Client Version 5.0.0 Build 1300600
vCenter Server Version 5.0.0 Build 1300600

This is just a note to myself about an issue i hit recently while working within vSphere 5.0 environment.
Here is the situation:


UserX is part of 2 groups , it does not matter if those are local groups or AD groups.
so i got:

domain\UserX belongs just to whose 2 groups.
To make this example really easy, i will use the base role of ‘Read-Only’.

This is example demonstrates the user that is part of 2 groups not being able to use the top right corner search functionality. Have in mind that i also tested the same example in vSphere 6.0 , there it works just fine.

Root VC element -> Permissions

1. Assign new permission for domain\groupA with Read-Only role (no propagation)

2. Login to vSphere, using ‘fat’ client and check that you see only top root VC part in the inventory.

3. Assign new permission for domain\groupB with Read-Only role (with propagation)

3. Having your vSphere client opened from step 2, you will notice that you can see entire inventory , you can check properties of vms, hosts etc..
BUT !!!

you will not be able to search for any vm in your inventory using the top right search element in vsphere client.

4. You will see that the permission list looks like this
domain\groupA no propagation read-only
domain\groupB propagation read-only

5. Go the first permission and switch it to propagation instead of no-propagation.
6. Go to second permission and switch it to no-propagation instead of propagation.

7. Result ? You will see all the inventory as previously, but now you are able to use the top right search function from vSphere client.

I have created a case for this behaviour at vmware support, apparently there are no plans to fix this in vsphere 5.0.

Receiving alerts from Nutanix Prism / Prism Central using REST with PowerShell

Today i wanted to add another section to my health check scripts, i wanted to get all unresolved, not acknowledged alerts from Nutanix Prism Central.
Nutanix has a nice REST explorer , you can check / test everything from your Nutnix Prism website and then transfer that to a script. Since i have already Prism Central, i don’t need to connect to each cluster Prism, you can do it though with this script. Just give the prism ip/url instead of prism central.
So below is a dirty script that does the work:

#to generate the password use:
#read-host -assecurestring | convertfrom-securestring | out-file C:\password.txt
$username = 'admin'
$PasswordFilePath = 'c:\password.txt'
$passwd= get-content $PasswordFilePath| convertto-securestring
$Header = @{"Authorization" = "Basic "+[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($username+":"+$password ))}
$PrismNode = ''
$uri= "https://${PrismNode}:9440/PrismGateway/services/rest/v1/alerts/?resolved=false&acknowledged=false"

add-type @"
   using System.Net;
   using System.Security.Cryptography.X509Certificates;
   public class TrustAllCertsPolicy : ICertificatePolicy {
       public bool CheckValidationResult(
           ServicePoint srvPoint, X509Certificate certificate,
           WebRequest request, int certificateProblem) {
           return true;
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

$myTimeZone=[System.TimeZoneInfo]::FindSystemTimeZoneById("Central European Standard Time")

#We can check TimeZones with: [system.timezoneinfo]::GetSystemTimeZones()
#[System.TimeZoneInfo]::ConvertTimeFromUtc( date , timezone )  -> converts time to given timezone
#(New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0).AddSeconds([math]::Floor(1430056923792344/1000000)) -> Converts time from timestamp , time was in Useconds hence /1000000

$response=Invoke-RestMethod -Uri $uri -Header $Header
Foreach($alarm in $response.entities)
#Create context hash for each alarm Binding ContextTypes with ContextValues
foreach ($name in $alarm.ContextTypes) 
#Watch out for empty entries ( no idea why there are null entries for Type that correspond to a value )
#Swap the {var} with value from our $context hash
	Foreach ($ContextType in ([regex]::Matches($alarm.message,'{(.*?)}')).Value )
		$alarm.message = $alarm.message -replace $ContextType,$context[$ContextTypeName]
$createdTime = ([System.TimeZoneInfo]::ConvertTimeFromUtc((New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0).AddSeconds([math]::Floor($alarm.createdTimeStampInUsecs/1000000)),$myTimeZone)).ToString()
$alarm | select-object -property @{n='CreatedTime';e={$createdTime}}, severity, alertType, message

In response we should get all the alerts like on the screenshot below:

Or even from out-gridview:


I am not 100% sure if i am doing it correct though, but i compared the text from prism, and that looks ok what i generate. I should spend more time on googling about the Nutanix REST API first i suppose 😉 but i had only few hours to do that. In the response we receive alert entities with a property ‘message’ , as you can see it keeps a placeholders for data like {ip_address} or {reboot_timestamp_str}, which we need to translate in order to see full message.


I might be wrong here or doing something in weird way but i guess outcome is accurate:
We take the {var} and replace it with value.
We have property contextTypes and contextValues, each entry from Type has its value in corresponding row from contextValues. I saw that sometimes contextType row is null, but the corresponding value has some string though. Looks like a duplicate text of another Type that is covered there, so i assumed that is safe not to take it with me to context hash.
That’s how example hash for an alert looks like that i am building:
So regex looks for {xxx} then replaces it with the corresponding value from context hash. For each alert i am building the context hash as alerts can be different and they have different contextTypes.
The time is also not exactly clear for me as ‘timestamp_str’ is way different than the createdTimeStampInUsecs. Anyway i have output done in the same way as Prism shows on the website.
Time in createdTimeStampInUsecs is an unix timestamp and has to be converted to ‘windows datetime’, this timestamp is written in microseconds hence we are dividing it by 1000000. After this is done , i am converting that date to my timezone.
All alerts that i am getting are those which are not acknowledged neither resolved. Authentication for Nutanix REST is described here for example:
So that concludes my first attempt to connect to Nutanix PRISM via REST using Powershell 😉
I hope it will help to start with other projects.

Root ramdisk, scratch KB

scratch partition