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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
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 http://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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
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 http://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:
1 2 3 4 5 6 7 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
gkulikowski#19:59:1; 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!
24 comments
Brilliant! Thank you!
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?
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.
I actually created some SQL queries instead and have them available to my team via SSRS. Thank you.
Awesome !! Thx a lot
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
to Clarify, the ‘real’ name of that machine would be WebServer42, as that I renamed it months ago.
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.
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 ?
Doubting you’re still looking for info on the NoEvent after all this time, but from what I can tell, that comes up if the VM is really old.
That is correct. If we would like to keep events/tasks for longer period of time we would have to change te virtual center database retention policy as described in documentation https://pubs.vmware.com/vsphere-51/index.jsp?topic=%2Fcom.vmware.vsphere.vcenterhost.doc%2FGUID-DD999601-2F41-498D-A577-DDC7C06648EE.html
Hi Greg,
Excellent work. I was able to find when VM was created. I checked script against VMware ESX 4.1 U3 and VMware ESXi 5.1 & 5.5
Best Regards
Wojciech
[…] if it is possible to find when virtual machine was created and by who. I found great post on Grzegorz blog. I tested it and it works from ESX 4.1 U3 up to ESXi […]
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
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.
This is superballs! Did exactly what I needed it to do, thanks so much!
you are welcome 😉
Great scripts! Thank you so very much.
Why in the hell should we have to do all of this heavy scripting to get a creation date for a VM? How in the hell is it that VMWare doesn’t simply have this in a VM’s summary? Typical half-ass engineering from half-ass VMWare. Ludicrous…
[…] The Code I’m using to Get the Event:Â Somewhat of a hack from https://grzegorzkulikowski.info/2012/11/27/when-was-vm-created-how-vm-was-created-and-who-has-create… […]