When was vm created, how vm was created and who has created this vm ?


I wanted to know where it is possible, who has created virtual machine. When the virtual machine was created and how it was created.
The idea is very simple. As an input i will take a virtual machine, and the output should be an object with few properties. VM can be created in several ways, right ? So i need to search for different events. When vm is being created/deployed it will create an event for this fact. Those events are as follows:

VmBeingCreatedEvent – Vm is created manually. Right-click New-VM. When we do VM in this way, a second event joins VmRegisteredEvent. So To check for vms there were manually created, we have to browse for events and check if 2 types of events have occured.
VmBeingDeployedEvent – When vm was deployed from template it will make this type of event
VmRegisteredEvent – When vm was registered, for example, open datastore browser, right click on vmx – register.
VmClonedEvent – When vm was cloned.

Since i have already written a function few days ago that queries for events i will use it here. I use Get-VMEvents, to query for specific events types regarding virtual machine object. So first let’s load this function to our powercli environment. Copy/paste this source code into your powershell/powercli/powergui/ise session.

function Get-VMEvents {
 <#
   .Synopsis

    Get events for an entity or for query all events.

   .Description

    This function returns events for entities. It's very similar to 
	get-vievent cmdlet.Note that get-VMEvent can handle 1 vm at a time.
	You can not send array of vms in this version of the script.

	.Example

    Get-VMEvents 0All -types "VmCreatedEvent","VmDeployedEvent","VmClonedEvent"

    This will receive ALL events of types "VmCreatedEvent","VmDeployedEvent",
	"VmClonedEvent". 
	
   .Example

    Get-VMEvents -name 'vm1' -types "VmCreatedEvent"

    Will ouput creation events for vm : 'vm1'. This was is faster than piping vms from
	get-vm result. There is no need to use get-vm to pass names to get-vmevents.
	Still, it is ok when you will do it, it will make it just a little bit slower 😉
	
   .Example

    Get-VMEvents -name 'vm1' -category 'warning'

    Will ouput all events for vm : 'vm1'. This was is faster than piping names from
	get-vm cmdlet. Category will make get-vmevent to search only defined category
	events. 
	
   .Example

    get-vm 'vm1' | Get-VMEvents -types "VmCreatedEvent","VmMacAssignedEvent"

    Will display events from vm1 which will be regarding creation events,
	and events when when/which mac address was assigned


	.Parameter VM

    This parameter is a single string representing vm name. It expects single vm name that
	exists in virtual center. At this moment in early script version it will handle only a case
	where there is 1 instance of vm of selected name. In future it will handle multiple as 
	well.
	
   .Parameter types

    If none specified it will return all events. If specified will return
	only events with selected types. For example : "VmCreatedEvent",
	"VmDeployedEvent", "VmMacAssignedEvent" "VmClonedEvent" , etc...
	
	.Parameter category

    Possible categories are : warning, info, error. Please use this parameter if you
	want to filter events.
	
	.Parameter All

    If you will set this parameter, as a result command will query all events from
	virtual center server regarding virtual machines. 

   .Notes

    NAME:  VMEvents

    AUTHOR: Grzegorz Kulikowski

    LASTEDIT: 11/09/2012
	
	NOT WORKING ? #powercli @ irc.freenode.net 

   .Link

    https://psvmware.wordpress.com

 #>

param(
[Parameter(ValueFromPipeline=$true)]
[ValidatenotNullOrEmpty()]
$VM,
[String[]]$types,
[string]$category,
[switch]$All
)
    $si=get-view ServiceInstance
    $em= get-view $si.Content.EventManager
    $EventFilterSpec = New-Object VMware.Vim.EventFilterSpec
	$EventFilterSpec.Type = $types
	if($category){
	$EventFilterSpec.Category = $category
	}
	
	if ($VM){
	$EventFilterSpec.Entity = New-Object VMware.Vim.EventFilterSpecByEntity
	switch ($VM) {
	{$_ -is [VMware.Vim.VirtualMachine]} {$VMmoref=$vm.moref}
	{$_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]}{$VMmoref=$vm.Extensiondata.moref}
	default {$vmmoref=(get-view -ViewType virtualmachine -Filter @{'name'=$VM}).moref }
	}
	$EventFilterSpec.Entity.Entity = $vmmoref
        $em.QueryEvents($EventFilterSpec) 
	}
	if ($All) {
	$em.QueryEvents($EventFilterSpec)
	}
}

Ok, once we have this loaded we can start actually querying with this function:

function get-vmcreationdate {
<#
   .Synopsis

    Gets where possible vm creation date.

   .Description

    This function will return object with information about  creation time, method, month,
	creator for particular vm. 
	VMname         : SomeVM12
	CreatedTime    : 8/10/2012 11:48:18 AM
	CreatedMonth   : August
	CreationMethod : Cloned
	Creator         : office\greg
	
	This function will display NoEvent value in properties in case when your VC does no
	longer have information about those particular events, or your vm events no longer have
	entries about being created. If your VC database has longer retension date it is more possible
	that you will find this event. 

	.Example

    Get-VMCreationdate -VMnames "my_vm1","My_otherVM"

    This will return objects that contain creation date information for vms with names
	myvm1 and myvm2
	
   .Example

    Get-VM -Location 'Cluster1' |Get-VMCreationdate

    This will return objects that contain creation date information for vms that are
	located in Cluster1
	
   .Example

    Get-view -viewtype virtualmachine -SearchRoot (get-datacenter 'mydc').id|Get-VMCreationDate

    This will return objects that contain creation date information for vms that are
	located in datacenter container 'mydc'. If you are using this function within existing loop where you
	have vms from get-view cmdlet, you can pass them via pipe or as VMnames parameter.

	.Example

    $report=get-cluster 'cl-01'|Get-VMCreationdate
	$report | export-csv c:\myreport.csv
    Will store all reported creationtimes object in $report array variable and export report to csv file.
	You can also filter the report before writing it to csv file using select
	$report | Where-Object {$_.CreatedMonth -eq "October"} | Select VMName,CreatedMonth
	So that you will see only vms that were created in October.


	.Example
	get-vmcreationdate -VMnames "my_vm1",testvm55
	WARNING: my_vm1 could not be found, typo?
	VMname         : testvm55
	CreatedTime    : 10/5/2012 2:24:03 PM
	CreatedMonth   : October
	CreationMethod : NewVM
	Creator        : home\greg
	In case when you privided vm that does not exists in yor infrastructure, a warning will be displayed.
	You can still store the whole report in $report variable, but it will not include any information about
	missing vm creation dates. A warning will be still displayed only for your information that there was
	probably a typo in the vm name.
	
	.Parameter VMnames

    This parameter should contain virtual machine objects or strings that represents vm
	names. It is possible to feed this function wiith VM objects that come from get-vm or
	from get-view. 


   .Notes

    NAME:  Get-VMCreationdate

    AUTHOR: Grzegorz Kulikowski

    LASTEDIT: 27/11/2012
	
	NOT WORKING ? #powercli @ irc.freenode.net 

   .Link

    https://psvmware.wordpress.com

 #>
 
param(
[Parameter(ValueFromPipeline=$true,Mandatory = $true)]
[ValidateNotNullOrEmpty()] 
[Object[]]$VMnames
)
process {
foreach ($vm in $VMnames){
$ReportedVM = ""|Select VMname,CreatedTime,CreatedMonth,CreationMethod,Creator
if ($CollectedEvent=$vm|Get-VMEvents -types 'VmBeingDeployedEvent','VmRegisteredEvent','VmClonedEvent','VmBeingCreatedEvent' -ErrorAction SilentlyContinue)
	{
	if($CollectedEvent.gettype().isArray){$CollectedEvent=$CollectedEvent|?{$_ -is [vmware.vim.VmRegisteredEvent]}}
	$CollectedEventType=$CollectedEvent.gettype().name
	$CollectedEventMonth = "{0:MMMM}" -f $CollectedEvent.CreatedTime
	$CollectedEventCreationDate=$CollectedEvent.CreatedTime
	$CollectedEventCreator=$CollectedEvent.Username
		switch ($CollectedEventType)
		{
		'VmClonedEvent' {$CreationMethod = 'Cloned'} 
		'VmRegisteredEvent' {$CreationMethod = 'RegisteredFromVMX'} 
		'VmBeingDeployedEvent' {$CreationMethod = 'VmFromTemplate'}
		'VmBeingCreatedEvent'  {$CreationMethod = 'NewVM'}
		default {$CreationMethod='Error'}
		}
	$ReportedVM.VMname=$CollectedEvent.vm.Name
	$ReportedVM.CreatedTime=$CollectedEventCreationDate
	$ReportedVM.CreatedMonth=$CollectedEventMonth
	$ReportedVM.CreationMethod=$CreationMethod
	$ReportedVM.Creator=$CollectedEventCreator
	}else {
		if ($?) {
			if($vm -is [VMware.Vim.VirtualMachine]){$ReportedVM.VMname=$vm.name} else {$ReportedVM.VMname=$vm.ToString()}
			$ReportedVM.CreatedTime = 'NoEvent'
			$ReportedVM.CreatedMonth = 'NoEvent'
			$ReportedVM.CreationMethod = 'NoEvent'
			$ReportedVM.Creator = 'NoEvent'
			
		} else {
			$ReportedVM = $null
			Write-Warning "$VM could not be found, typo?"
		}
	}
	$ReportedVM
}
}
}

Ok, so now we have 2 functions that we can use.
Get-VMEvents that will query for events regarding a VM, and Get-VMCreationdate that uses the first function. There are couple of ways to use it.
The most basic approach i guess will be:

greg#19:58:02> get-vm testvm55 | Get-VMCreationDate

VMname         : testvm55
CreatedTime    : 10/5/2012 2:24:03 PM
CreatedMonth   : October
CreationMethod : NewVM
Creator        : home\Gregu

It also gives you a warning when you are querying a vm that does not exist in your infrastructure.

gkulikowski#19:59:14> get-vmcreationdate -VMnames 'testvm55','testvm112','this_vm_does_not_exist'

VMname         : testvm55
CreatedTime    : 10/5/2012 2:24:03 PM
CreatedMonth   : October
CreationMethod : NewVM
Creator        : home\gregu

VMname         : testvm112
CreatedTime    : 11/25/2012 4:14:11 PM
CreatedMonth   : November
CreationMethod : RegisteredFromVMX
Creator        : home\gregu

WARNING: this_vm_does_not_exist could not be found, typo?

When you will query for a VM that no longer has event information about being created, it will result in sending an object with a vm name and other properties with NoEvent value. It’s up to you then what do in that case. Maybe you want to query Active Directory and check registration there when NoEvent is returned. Maybe you do not have AD in your environment, and you are fine with marking creation date as a date of the oldest event for a virtualmachine. Like i said, its up to you what to do in this case.
All sorts of selecting vms are possible to feed Get-VMCreationdate
get-vm -Location X | …
get-cluster ‘some_cluster’ | …
get-datacenter ‘X’ | …

And of course you can filter information about creation dates for your virtual machines. Let’s say that you have already a report

$vms=get-vm -Location ‘My_folder1’
$report=Get-VMCreationdate -VMnames $vmnames

$report |Where-Object {$_.CreatedMonth -eq “July”} | Select VMname
Would for example output only vm names for vms that where created in July.

$report |Where-Object {$_.Creator -eq “Grzegorz”} | Select VMname
Would for example output only vm names for vms that where created by Grzegorz.

$report |Where-Object {$_.CreationMethod -eq “RegisteredFromVMX”} | Select VMname
Would for example output only vm names for vms that were created using method: Registered from vmx.

$report |Where-Object {$_.CreationMethod -eq “Cloned”} | Select VMname
Would for example output only vm names for vms that were cloned.

If you want to store your report as an CSV there is no problem with that:
$report | Export-csv c:\myreport.csv

You will probably see a lot of vms without any data , i mean they will have NoEvent value. How to fight with that ? If you want to track the creationdate, and have this information always available for virtualmachine, you can for example create an annotation with name : CreatedDate , and you can check your vms once a day to see if a vm does not have this annotation completed. If there is no information in the annotation you can simply use Get-VMCreationdate, to get this information and store i within vm annotation field. If after some months you VC will lose information about creation events, created dated will be still in the annotation. If you have any other ideas about this, write a comment !

What can be done more ? You will notice that command like :
get-vm|get-vmcreationdate
Will take some time. It should take 1.5 second to obtain information for 1 vm. If you have a large infrastructure like 1000,5000, and more vms it will take some time to complete this report. There is a way though to obtain report for ALL vms in your infrastructure in around ~20-30 seconds i believe. Unfortunately i don’t have that much free time to write it. But if you want to try it, please share your work with us in comments or thoughts about this idea.
You can use get-vmevents with -all switch and -types that will indicatate creation of virtual machine.
$creationevents=get-vmevents -all -types ‘VmBeingDeployedEvent’,’VmRegisteredEvent’,’VmClonedEvent’,’VmBeingCreatedEvent’
In order to complete this it will need around 2-3 seconds.
This way of doing this is very quick BUT, it will include information of virtual machines that no longer exists in your infrastructure, so we have to filter these out.
$creationevents array will include events objects that have vm moref property. So you have to store it somewhere.
$morefs=$creationevents|%{$_.vm.vm}
Like this for example
Then you have to compare those morefs with morefs for vms that you actually have in your vc , for example using compare-object.
Once this is done You can now feed get-vmcreationdate with those events that have proper moref property.
When i will have more time i will try this idea out 😉 For now, no time ;(
Big thanks goes to Bartosz Bielawski for helping me out with some parts of the script!

Advertisements

22 thoughts on “When was vm created, how vm was created and who has created this vm ?

  1. I am having a difficult time trying to figure out how to use this information. I want to be able to run a report for all creation methods and have it go to a file so that I can sort. It looks like I am after the 2nd function you have shown. I have loaded it, but am having trouble going from there. How do I actually run the report needed and save the output?

  2. Hi, can you let me know what is wrong ? It’s not clear for me what is not working. Post includes information how to invoke report and save it to file.

  3. Love your script! Question though, when I run it, it shows me the original name of the VM, not the current name.

    EG,

    Machine42_NEW 10/1/2013 18:27 October Cloned Durdle

    I used

    $report=get-vm|get-vmcreationdate
    $report | export-csv x:\report.csv

      • Hi Rob, thanks for comments. To be honest i was not thinking about this possibility. Like you wrote, this will be his default behaviour in returning the names due to the fact that vm name is being collected from event from the past.
        If you would like to get name not from the event but the current vm, please look at line 113 in this script.
        $ReportedVM.VMname=$CollectedEvent.vm.Name
        Please change this line to
        $ReportedVM.VMname=$vm.name

        i think that will help.

  4. Hi good job. Exactly what i was looking for.

    But don’t work in my case 😦
    vSphere is running version 4.1

    >get-vm Vm-test |get-vmcreationdate
    VMname : Vm-test
    CreatedTime : NoEvent
    CreatedMonth : NoEvent
    CreationMethod : NoEvent
    Creator : NoEvent

    Any Idea ?

  5. Pingback: Find when Virtual Machine was created using SQL query | w o j c i e h . n e t
  6. Greg, I have this error when load the function Get-VMEvents. Appreciate if someone can guide me through it.

    The ‘<' operator is reserved for future use.
    At line:60 char:36
    + $EventFilterSpec = New-Object < <<<VMware.Vim.EventFilterSpec
    + CategoryInfo : ParserError: ( if ($VM){
    >> $EventFilterSpec.Entity = New-Object VMware.Vim.EventFilterSpecByEntity
    >> switch ($VM) {
    >> {$_ -is [VMware.Vim.VirtualMachine]} {$VMmoref=$vm.moref}
    >> {$_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl
    ]}{$VMmoref=$vm.Extensiondata.moref}
    >> default {$vmmoref=(get-view -ViewType virtualmachine -Filter @{‘name’=$VM}).moref }
    >> }
    >> $EventFilterSpec.Entity.Entity = $vmmoref
    >> $em.QueryEvents($EventFilterSpec)
    >> }
    >> if ($All) {
    >> $em.QueryEvents($EventFilterSpec)
    >> }
    >> }
    >>
    The ‘<' operator is reserved for future use.
    At line:2 char:43
    + $EventFilterSpec.Entity = New-Object < <<<VMware.Vim.EventFilterSpecByEntity
    + CategoryInfo : ParserError: (<:OperatorToken) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RedirectionNotSupported

    • could you please paste what you are trying to load in powershell ? I think either you have added something somewhere, or you are pasting it with some special characters maybe. When i copy/paste what is in the post i got no errors. Would be best if you would just paste on some pastebin, the code you are trying to execute.

      • Thank you for reply. I copy and paste the whole function, got error then I took out the comment & examples part and got the same error. I used 3 different machines to load the same function and got the same error. Here is the shorter version of the function
        ===============================================
        PS C:\MyPS> function Get-VMEvents {
        >> param(
        >> [Parameter(ValueFromPipeline=$true)]
        >> [ValidatenotNullOrEmpty()]
        >> $VM,
        >> [String[]]$types,
        >> [string]$category,
        >> [switch]$All
        >> )
        >> $si=get-view ServiceInstance
        >> $em= get-view $si.Content.EventManager
        >> $EventFilterSpec = New-Object VMware.Vim.EventFilterSpec
        >> $EventFilterSpec.Type = $types
        >> if($category){
        >> $EventFilterSpec.Category = $category
        >> }
        >>
        The ‘<' operator is reserved for future use.
        At line:12 char:36
        + $EventFilterSpec = New-Object < <<<VMware.Vim.EventFilterSpec
        + CategoryInfo : ParserError: ( if ($VM){
        >> $EventFilterSpec.Entity = New-Object VMware.Vim.EventFilterSpecByEntity
        >> switch ($VM) {
        >> {$_ -is [VMware.Vim.VirtualMachine]} {$VMmoref=$vm.moref}
        >> {$_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl
        ]}{$VMmoref=$vm.Extensiondata.moref}
        >> default {$vmmoref=(get-view -ViewType virtualmachine -Filter @{‘name’=$VM}).moref }
        >> }
        >> $EventFilterSpec.Entity.Entity = $vmmoref
        >> $em.QueryEvents($EventFilterSpec)
        >> }
        >> if ($All) {
        >> $em.QueryEvents($EventFilterSpec)
        >> }
        >> }
        >>
        The ‘<' operator is reserved for future use.
        At line:2 char:43
        + $EventFilterSpec.Entity = New-Object < <<<VMware.Vim.EventFilterSpecByEntity
        + CategoryInfo : ParserError: (<:OperatorToken) [], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : RedirectionNotSupported

  7. JB. you are not pasting the function you are trying to load. You are pasting some fragment of function + error output. Can you please just paste the entire script you are trying to load on some pastebin ?

    • Sorry, I misunderstood.
      ==========================================================
      function Get-VMEvents {
      param(
      [Parameter(ValueFromPipeline=$true)]
      [ValidatenotNullOrEmpty()]
      $VM,
      [String[]]$types,
      [string]$category,
      [switch]$All
      )
      $si=get-view ServiceInstance
      $em= get-view $si.Content.EventManager
      $EventFilterSpec = New-Object VMware.Vim.EventFilterSpec
      $EventFilterSpec.Type = $types
      if($category){
      $EventFilterSpec.Category = $category
      }

      if ($VM){
      $EventFilterSpec.Entity = New-Object VMware.Vim.EventFilterSpecByEntity
      switch ($VM) {
      {$_ -is [VMware.Vim.VirtualMachine]} {$VMmoref=$vm.moref}
      {$_ -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]}{$VMmoref=$vm.Extensiondata.moref}
      default {$vmmoref=(get-view -ViewType virtualmachine -Filter @{‘name’=$VM}).moref }
      }
      $EventFilterSpec.Entity.Entity = $vmmoref
      $em.QueryEvents($EventFilterSpec)
      }
      if ($All) {
      $em.QueryEvents($EventFilterSpec)
      }
      }

    • I found the issue. I just need to remove all and from both functions. Appreciate your responses and many thanks to creator.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s