Function for getting report on annotation in VirtualCenter server clusters or particular cluster

I described one way of getting this report in earlier post. If you want to use it as a function… there you go 😉
You can always use get-help greg-get-annotations , if you are in powershell session and want to check the syntax etc..
Report will also include the normal notes section. I’ve tested it in some environments, for example it took 38 sec to complete it in VI where there was ~1500vms, including creation of the csv.

function greg-get-annotations {
Greg-get-annotations function stores information about annotation fields for vms in given
cluster or in all clusters in VC. It stores the result in an arraylist $vms, you can either create
a csv report from this object or display it on screen
greg-get-annotations |export-csv -NoTypeInformation c:\file1.csv will export it to csv file etc...
greg-get-annotations |format-table VMname,Cluster,CreatedOn,Notes will just display on screen a table with
annotations that include : vm name, its cluster and field "CreatedOn" and Notes

.PARAMETER clustername
Specifies the clustername against wchi report will be built

greg-get-annotations -clustername 'cluster01'|Export-Csv c:\annotation-report.csv
Will procude report on vms that resides in 'cluster01' and store it in csv file

greg-get-annotations -clustername 'cluster01'|ft *
Will procude report on vms that resides in 'cluster01' output it to screen

greg-get-annotations |Export-Csv c:\annotation-report.csv
Will procude report on vms that resides in all clusters and output it to screen

Without specified -clustername switch, it will do report regarding all clusters in VC

AUTHOR: Grzegorz Kulikowski
LASTEDIT: 05/30/2011

param ([string]$clustername)
if(!($clustername)){$clusters=Get-Cluster}else{$clusters=Get-Cluster $clustername}
$VMs=New-Object Collections.ArrayList
foreach ($cluster in $clusters)  {
foreach ($vmview in (get-view -ViewType VirtualMachine -SearchRoot $ {
$vm=New-Object PsObject
Add-Member -InputObject $vm -MemberType NoteProperty -Name VMname -Value $vmview.Name
Add-Member -InputObject $vm -MemberType NoteProperty -Name Notes -Value $vmview.Config.Annotation
Add-Member -InputObject $vm -MemberType NoteProperty -Name Cluster -Value $cluster.Name
foreach ($CustomAttribute in $vmview.AvailableField){
Add-Member -InputObject $vm -MemberType NoteProperty -Name $CustomAttribute.Name -Value ($vmview.Summary.CustomValue | ? {$_.Key -eq $CustomAttribute.Key}).value
return $VMs

Add-Member -InputObject $vm -MemberType NoteProperty -Name Notes -Value $vmview.Config.Annotation

Custom report about Custom Fields in virtual infrastructure

This time i wanted to create a report on information which is put in the custom fields for my vms. You can do it very easy using the :

get-vm VM1 | get-annotation

What is more , you can specify which particular custom field you want to retrieve, for example if you cant a custom field called ContactPerson you could write:

get-vm VM1 | get-annotation -CustomAttribute "ContactPerson"

Now it is ok to use it like this, but using that : get-vm | % { get-annotation } would take you quite a while when ran in VI > 100 vms, and even more in > 1000 vms.
I have written short one liner to get all the data and put it into a csv file so i could minupulate data using filters and conditional formatting.

$(foreach ($cluster in get-cluster)  { get-view -ViewType VirtualMachine -SearchRoot $ | % { $vVM=$_; $_.Summary.CustomValue | select @{N="VM Name";E={$vVM.Name}},@{N="Cluster";E={$}},Value,Key,@{N="Key Name";E={$vKey=$_.Key;($vVM.AvailableField|?{$_.Key -eq $vKey}).Name}} }}) | export-csv c:\customfields.csv

You will receive a file in c:\customfields.csv , which will include information as on the screenshot below:

Report is generated in about 8-10 sec on a 1000vms + infrastructure, so it is faster than with the get-annotation. Feel, free to modify it, add other custom columns so it will meet your expectations.
I had to go to two places in order to complete this report, 1 is the Summary.CustomValue section from the get-view virtualmachine object, and second is the AvailableFiled section. Summary.CustomValue will tell you what values are set on which Keys(Key number), and using AvailableField you match a pair KeyNo=KeyName. I am doing a cluster loop, to get the have the information in which cluster vm resides. If needed you can change this to datacenters, or just use without cluster/datacenter container.
The above example was pulling only existing annotations data, so you will not see any vm there which did not have the custom fields filled out. I made a new script to pull data on each vm, each annotation even if the vm does not have any annotation data. What’s more, instead of having each row on “annotation entry” for vm, i have create now an output where you have 1 vm per row, and in columns you have all annotations. I hope this one will be more useful for everybody.

foreach ($cluster in get-cluster)  {
    foreach ($vmview in (get-view -ViewType VirtualMachine -SearchRoot $ {
        $vm=New-Object PsObject
        Add-Member -InputObject $vm -MemberType NoteProperty -Name VMname -Value $vmview.Name
        Add-Member -InputObject $vm -MemberType NoteProperty -Name Cluster -Value $cluster.Name
        foreach ($CustomAttribute in $vmview.AvailableField){
            Add-Member -InputObject $vm -MemberType NoteProperty -Name $CustomAttribute.Name -Value ($vmview.Summary.CustomValue | ? {$_.Key -eq $CustomAttribute.Key}).value
$VMs|Export-Csv c:\annotation-report.csv

I decided to build my own object, and then fill it with all properties. It is building property columns dynamically, so when you have 5 you get 5 columns, if 10 then 10 etc…
And what is more important 😉 it is easier to read this one !

Custom report, checking notes/description for vm, Active Directory description, ManagedBy, Operating System and exporting to csv

This time i wanted to build a report to check which vms are missing the notes/description section. If you have large virtual infrastructure it is very handy to have information about vm, what is its role. In case i don’t have notes/description field in VM, i would go to AD and check if the object has description. I would also like to know who to contact in case it does not have the description i will put data from ManagedBy field in AD. I would also like to know what operating system the vm has, and if it is powered on or off. At the end, i want to have a csv file with this information, so i can quickly edit it in Excel, or other spreadsheet sotware, apply some filters, conditional formatting and so on. It could be done also via powershell using the excel object for instance, and generating data directly to excel spreadsheet but it would take more time to write this at this point for me than applying filters and conditional formatting(1 minute).

$(foreach ($cluster in get-cluster) { get-view -viewtype VirtualMachine -SearchRoot $ | select @{N="VM name";E={$_.Name} },@{N=
"PowerState";E={$_.Summary.Runtime.PowerState}} ,@{N="Guest OS";E={$_.Config.GuestFullName} } , @{N="Cluster";E={$} },@{N="VM .Notes";E={$_.Summary.
Config.Annotation} }, @{N="AD Description";E={(Get-QADComputer $_.Name).Description} },@{N="AD ManagedBy";E={((Get-QADComputer $ | Get-QADUser)
.DisplayName} } }) | export-csv -NoTypeInformation c:\report.csv

I am pulling data cluster after cluster:
foreach ($cluster in get-cluster)
Pulling data for virtual machines in that particular cluster:
get-view -viewtype VirtualMachine -SearchRoot $
Building custom colums :
select @{N=”VM name”;E={$_.Name} -> this should be self explanatory, at this moment i am in a loop where i am receiving vms for cluster, so $_ is the view for VM, $_.Name is the name field in the vm view
I want to have in report the powerstate information :
I want to have information about the operating system:
@{N="Guest OS";E={$_.Config.GuestFullName} }
Information about in which cluster vm resides :
@{N="Cluster";E={$} }
Finally i want to see if the notes have been filled out or not:
@{N="VM .Notes";E={$_.Summary.Config.Annotation} }

In case the notes section is not filled out, i want to see what’s in AD description field:
@{N="AD Description";E={(Get-QADComputer $_.Name).Description} }
If that description would be empty i would like to know who to contact in order to get this information so:
@{N="AD ManagedBy";E={((Get-QADComputer $ | Get-QADUser).DisplayName} }
At the end i close whole output $() in sub-expression to pipe it to export-csv

I was using here Quest cmdlets to query AD. You can download it for free at
Make sure that if you are using them in a script you will add them first by using this:
Add-PSSnapin Quest.ActiveRoles.ADManagement


new-drsrule Unable to cast object of type ‘VMware.Vim.ClusterVmHostRuleInfo’ to type ‘VMware.Vim.ClusterAntiAffinityRuleSpec’.

I was trying to create a new drs vm-vm anti affinity rule and got this red error :

PowerCLI C:\> New-DrsRule -Cluster xyz -Name AA-vm1,vm2 -KeepTogether:$false -VM vm1,vm2
New-DrsRule : 5/14/2012 9:30:23 AM New-DrsRule Unable to cast object of type 'VMware.Vim.ClusterVmHostRuleInf
o' to type 'VMware.Vim.ClusterAntiAffinityRuleSpec'.
At line:1 char:12
+ New-DrsRule <<<< -Cluster xyz -Name AA-vm1,vm2 -KeepTogether:$false -VM vm1,vm2
+ CategoryInfo : NotSpecified: (:) [New-DrsRule], ViError
+ FullyQualifiedErrorId : Client20_ClientSideTaskImpl_ThreadProc_UnhandledException,VMware.VimAutomation.ViCore.Cm

Still , i do not know if there is a way to fix this , or is this just a bug and will be fixed in new release of powercli.
This error is only “informing” us, and the rule is still created on this cluster, so i would say that this is only a minor issue. But if you are not happy that it outputs those nasty red errors, you can do other function that ads a vm-vm rule on a cluster , i will write function later, but if want to write it yourself this should be more than enough in order to write it:

$spec2 = New-Object VMware.Vim.ClusterConfigSpecEx
$spec2.rulesSpec = New-Object VMware.Vim.ClusterRuleSpec[] (2)
$spec2.rulesSpec[0] = New-Object VMware.Vim.ClusterRuleSpec
$spec2.rulesSpec[0].operation = "add"
$spec2.rulesSpec[0].info = New-Object VMware.Vim.ClusterAffinityRuleSpec            
$spec2.rulesSpec[0].info.enabled = $true
$spec2.rulesSpec[0] = "AA-vm1-vm2"
$spec2.rulesSpec[0].info.userCreated = $true
$spec2.rulesSpec[0].info.vm = (get-vm vm1,vm2|get-view) |% {$_.moref}  
(Get-View -Id (get-cluster xyz).id).ReconfigureComputeResource_Task($spec2, $true)

# object for AA rule is VMware.Vim.ClusterAntiAffinityRuleSpec
# you can put as many vms as you want in info.vm section...

Today is diablo3 world premiere 😉 so um… i will not write this function today 😉
Why this error is showing up ? that’s because i have used a VM-TO-HOST rule on cluster. Once you define a rule like that, new-drsrule will output this error while creating a VM-VM rule on that cluster. I have observed this on powercli 5.0.1. Same for remove-drsRule, when you will specify a vm-host rule, it will also FAIL, not just by screaming with error, but the deleting will not proceed. I will also write a function for this , but like i said not today 😉
I will post a pack of drs functions shortly with add/remove and so on. This topic was mentioned on vmware communities, you can open it using this link and check how to reproduce this error step by step.
VMware has confirmed this bug, and wrote that it is fixed in new release. Nice!

Virtual Center , permissions, adding, removing, inheriting, groups, users and such

I was reading today about permissions on different level in virtual center. While reading i was doing many different tests in my tiny lab. I wanted to share my results with you, as there are many different opinions in internet. I thought i will make this little presentation to sort this out.
First of all, check the official vmware documentation about permissions,rights, etc… using this LINK
Lest’s start from beggining.

We have virtual center VC, datacenter LAB2, and some esx host called . User “greg” resides in group “leastpriv” and “mostpriv”. For group leastpriv will always contain less privileges than mostpriv.
At this moment i have put those two groups privileges(leastpriv with role no access, and mostpriv with Administrator) on a vc level and set them to propagate to lower levels.
Example #1
We have restricted access to user “greg” giving his group leastpriv no access role on VC level. In the same time we have given him Administrator role by putting his group mostpriv. In that case the sum result of those 2 privileges will equal to LESS restrictive. So the user greg is still able to login to VC, as on the screenshot below and he has administrator role.

Now let’s go back to the documentation that about roles,permission… it says:
“If multiple group permissions are defined on the same object and the user belongs to two or more of those groups, two situations are possible:

If no permission is defined for the user on that object, the user is assigned the set of privileges assigned to the groups for that object.

If a permission is defined for the user on that object, the user’s permission takes precedence over all group permissions.”
So we have here situation where we have added 2 groups where our user “greg” is. No user permission was defined on the object. In that case as the document says, we get the set of privileges assigned to the groups for that object. So, some people may thin that we would receive least permissions here, but no, we get the whole set which results in LEAST restrictive privileges. Let’s see what happens next.
Example #1a
We have decided for some reason to give our user greg, ‘no access’ role to the host We have placed this user level privilege directly on our esx object.

User Greg will have now “no access” to the host. As per vmware documentation, when a user-level privilege is set on a object which has also some group-level privilege in place, the user-level will take place. Our user had an Admin and No access, privileged on the host, both were defined on VC, and they have propagated to our esx object. But when we will specify a privilege for user, his rights that came from groups doest not count anymore.
Now let’s add some things.
Example #2

We have created some vm folders. We can see that user “greg” can acess them, and as we know he has effective Adminsitrator role(i have removed the no access on host for him from example#1).

We have given Read-Only role for leastpriv group. User greg can only view contents of the folder vmfolder2. This role is more important here than the administrator rol that is on the mostpriv group, because this privilege was set on the child object. As per documentation, when you have a privilege on group that is propagatating  the from the parent to child,  and the child has defined another privilege on it directly. The childs prvilige will take place. In our case, “greg” will not have the adminsitrator anymore on vmfodler2.
Don’t get confused by active option “Create new virtual machine”.

If you will click it , you will get an error. Our user, have read-only access there.

So here, we have defined leastpriv group on a folder vmfolder2 level with a read-only. Greg could not create any vms in that folder, but he still has administrator privileges in vms that are residing in this folder. As per screenshots we have here group leastpriv that was defined on folder level and the mostpriv which was defined on vc level. As i was previously writing, in this situation we will have effective privileges of the group set in which greg resides. So why this situation is wrong ? Well, that’s because setting administrator priviliges on VC level is not a best idea. If you will put many different groups on VC level with administrator privileges and set them to propagate, you can loose hours,days to find out why some user has still access to something. You should set only trusted administrators group on VC level as they are gaining also specific rigths on the root level like custom fields, licenses, roles, sessions, statistic intervals.

I have set leastpriv group with read-only rights on vmfolder2 level. All vms in that folder for greg should be now read-only. As do not have any other permissions propagating for us from upper levels. I have also set on the vm2 object directly , his group mostpriv with Administrator privileges. Now Greg can fully administrate vm1, and has only read-only access to vm1. Group mostpriv is attached directly to vm2 which is child of its parent(vmfolder2). When attaching some role to child directly, it will ignore roles that were propagated to it.
Let’s make something more weird 😉

Now, few seconds ago greg was Administrator, but now he has no access role. Once again, this happend because, even if other group is giving the user some Administrator ritgts(mostpriv) that is attached directly on the object, which ignores the limitation of group leastpriv(which was propagating its priviliges to child), still when you will attach privilege directly on child using a USER-level privilege, then the user selected privilige will be used.

I know it’s long and it might seem complicated, but i tried very hard to explain this ;). For me reading vmware documentation few times worked best. I tried to read serveral different documents, but after reading vmware guide i finally understood it. What’s worth also mentioning:

Most inventory objects inherit permissions from a single parent object in the hierarchy. For example, a datastore inherits permissions from either its parent datastore folder or parent datacenter. Virtual machines inherit permissions from both the parent virtual machine folder and the parent host, cluster, or resource poolsimultaneously. To restrict a user’s privileges on a virtual machine, you must set permissions on both the parentfolder and the parent host, cluster, or resource pool for that virtual machine.

That’s from the vmware guide, also in this guide(i posted link in the beggining of the post), you will find great diagram of vSphere inventory hierarchy, and how objects can propagate permissions.
It’s 3 am now, so i know i made a lot of typos and so on.. Will try to correct the tomorrow.
Please do comment, or write me a message if you think something might be wrong what i have written, or if have confused you even more ;]

Quicky#1 Get csv report on virtual machine partitions in cluster/multiple clusters

I was asked today how to do that kind of report. It’s really handy to have one of those, and i was surprised that i haven’t wrote about that so far on my blog. So here it is 😉

$(foreach ( $cluster in 'xyz1','yxx2' ) { get-view -SearchRoot (get-cluster $cluster).id -viewtype VirtualMachine -Filter @{"Summary.Runtime.PowerState"="poweredOn";"Config.Template"="False"}  | % {$esxhost=(get-view ($_.Summary.Runtime.Host)).name;$vmname=$_.Name;$_.Guest.Disk | select @{N="VMName";E={$vmname}},DiskPath,@{N="CapacityGB"; E={[Math]::Round($_.Capacity/1GB,2)} },@{N="FeeSpaceGB";E={[Math]::Round($_.FreeSpace/1GB,2)}},@{N="Cluster";E={$cluster} },@{N="Host";E={$esxhost} } }  })  | Export-Csv "c:\ReportVMsDiskUsage.csv" -NoTypeInformation

Yes, i know 😉
So what it does. It will output a csv file in my example to c:\ReportVMsDiskUsage.csv”. Contents are : Name of VM, disk letter/path, Capacity in Gb, FreeSpace in GB, Cluster name on which vm resides, Host name on which vm resides.
This one liner looks bit complicated but once disassembled it’s very easy to understand what i am doing.
I am using the sub-expression $ ( ) | Export-csv … . I did that because i want the whole output in my csv file. Without first getting the whole output it’s very hard i suppose to use the export-csv to put it to 1 file. If you want this to run against 1 cluster or more clusters, just adjust the strings in the foreach($cluster in ….) .
It can be very easy to adjust this one liner to ignore cluster, do the report on the highest level. I am using switch -SearchRoot from get-view and giving him the location of get-cluster X. If you do not want to narrow down the results, just don’t use the -SearchRoot, and it will give you output for all vms that are:
-Filter @{“Summary.Runtime.PowerState”=”poweredOn”;”Config.Template”=”False”}
Powered On, and are not tempaltes.
Before sending the output ” $_.Guest.Disk | ….” further , i also grab all other needed properites, like vmname,esxhost.
It also useful to check the output first before sending it to export-csv. So let’s take a look by running :

foreach ( $cluster in 'xyz1','yxx2' ) { get-view -SearchRoot (get-cluster $cluster).id -viewtype VirtualMachine -Filter @{"Summary.Runtime.PowerState"="poweredOn";"Config.Template"="False"}  | % {$esxhost=(get-view ($_.Summary.Runtime.Host)).name;$vmname=$_.Name;$_.Guest.Disk | select @{N="VMName";E={$vmname}},DiskPath,@{N="CapacityGB"; E={[Math]::Round($_.Capacity/1GB,2)} },@{N="FeeSpaceGB";E={[Math]::Round($_.FreeSpace/1GB,2)}},@{N="Cluster";E={$cluster} },@{N="Host";E={$esxhost} } }  }

You will see report on each vm . from each cluster. Then we need to get the WHOLE output as one and send it to export-csv, and this is why i used
sub-expression $() | export-csv… to grab first the whole output. I got loops for each cluster so , i cant export the loop to export-csv, as the file will be overwritten as the end by the last loop output. You could ommit the export-csv and the sub-expression here by sending the output straight to file using ” >> file.csv ” , but in this case you would have to prepare some [string]$line, which will have commas in it so it would look like a csv file.
If you find any bug in my scripts or you know more efficient way to do things describes in my posts, please do comment and share your experience.

Change the syslog server for esxi5 box using powercli

Let’s say that we would like to change the syslog server to which our esxi5 boxes is sending logs in some particular cluster.

$changedValue = New-Object VMware.Vim.OptionValue[] (1)
$changedValue[0] = New-Object VMware.Vim.OptionValue
 $changedValue[0].key = ""
 $changedValue[0].value = "tcp:/your-syslog-ip:514"

Get-View -ViewType HostSystem -Searchroot (Get-Cluster "your-cluster-name").Id | %{
  $optMgr = Get-View $_.ConfigManager.AdvancedOption

Now check if we have updated values

get-view -viewtype HostSystem -SearchRoot (get-cluster "our-cluster").id | % { get-view $_.ConfigManager.AdvancedOption | select -ExpandProperty Setting |?{$_.Key -like ""} }

Many thanks to LucD for pointing me out a better way to go with updating this value!