Get VM Last Use Time

Table of contents

I don’t know about you, but my Hyper-V box has a number of virtual machines. Many of them created for ad-hoc or testing purposes. I suspect you might be in the same boat. In most cases a running virtual machine indicates it is “in production”. But what about the other machines? How can I tell when they were last used? We can get creation time easy enough using PowerShell and the Get-VM cmdlet.

PS C:\> get-vm | sort CreationTime | Select VMName,CreationTime,Notes | out-gridview -title "VM Creation Report"

I piped the result to Out-Gridview to make easier to re-sort or perform additional filtering. You can see my results in Figure 1.

Figure 1

That can be useful information but I want to know when the virtual machine was last fired up. My initial thought was to check the timestamp on the virtual machine configuration file. But it is possible for someone to adjust the configuration without actually starting the virtual machine. Thinking about it, the best solution would be to look at the timestamp on the VHD or VHDX file. This assumes that you have Windows file system access to the associated disk files. Since a virtual machine might have multiple associated hard disk files, I decided to simply check the first one in the array, working under the assumption that this is an active drive. The disk file should be updated whenever the virtual machine is running so looking at the timestamp of the disk file should tell me when the virtual machine was last used. Here’s one way.

Get-VM | Select vmname,@{Name="HDD";Expression={$_.Harddrives[0].path}},
@{Name="LastUse";Expression={(get-item ($_.Harddrives[0].path)).LastWriteTime}}| sort LastUse | Out-GridView -Title "VM Last Use"

All I’ve done is create a custom property called LastUse which gets the first hard drive from the HardDrives property and then retrieves the LastWriteTime property. You can see my result in Figure 2

Figure 2

As you can see I have a couple virtual machines missing hard disk files. This could be a configuration issue or perhaps a sign that these machines are obsolete. In either case I have actionable data. Dates are nice but it might also be helpful to see some aging. That is easy enough using PowerShell.

Get-VM | Select VMName,CreationTime,
@{Name="CreationAge";Expression={[int]((Get-Date) - $_.CreationTime).TotalDays}},
@{Name="LastUse";Expression={(Get-Item ($_.Harddrives[0].path)).LastWriteTime}},
@{Name="LastUseAge";Expression={[int]((Get-Date) - ((Get-Item ($_.Harddrives[0].path)).LastWriteTime)).TotalDays}},
Notes |Sort LastUse | Out-GridView -Title "VM Aging Report"

This one-line PowerShell command subtracts the creation time and last write time properties from the current time to create a TimeSpan object. I get the TotalDays property and cast it as an integer which has the effect of rounding the value as you can see in Figure 3.

Figure 3

In the grid view I clicked on the LastUseAge heading to sort. I sent output to Out-Gridview but you could have just as easily exported to a CSV file or created an HTML report. Allow me to wrap up with a PowerShell script you can run to create a last use report. This script focuses on when a virtual machine was last used which was my intent all along.

#requires -version 3.0
#requires -modules Hyper-V

<#
.Synopsis
Find a virtual machine last use date.
.Description
This command will write a custom object to the pipeline which should indicate
when the virtual machine was last used. The command finds all hard drives that
are associated with a Hyper-V virtual machine and selects the first one. The
assumption is that if the virtual machine is running the hard drive file will
be changed. The function retrieves the last write time property from the first
VHD or VHDX file.

You can pipe a collection of Hyper-V virtual machines or specify a virtual
machine name. Wildcards are supported. The default is to display last use data
for all virtual machines. This command must be run on the Hyper-V server in
order to get file system information from the disk file.

The command requires the Hyper-V module running in PowerShell 3.0.

.Example
PS C:\> c:\scripts\get-vmlastuse.ps1 xp*

VMName LastUse LastUseAge
------ ------- ----------
XP Lab 4/20/2013 10:31:56 AM 39.22:46:56.5270998

Get last use information for any virtual machine starting with XP.

.Example
PS C:\> get-vm | where {$_.state -eq 'off'} | c:\scripts\get-vmlastuse

VMName LastUse LastUseAge
------ ------- ----------
CHI-Client02 4/20/2013 10:39:42 AM 39.22:39:37.9838225
DCDemo 5/3/2013 1:13:24 PM 26.20:05:56.2528664
Demo Rig 3/3/2013 2:32:17 PM 87.18:47:02.8858740
DemoVM 5/29/2013 8:01:49 PM 13:17:31.7162000
...

Get last use information for any virtual machine that is currently off.

.Inputs
String or Hyper-V Virtual Machine
.Outputs
custom object
.Link
Get-VM
#>

[cmdletbinding()]
Param (
[Parameter(Position=0,
HelpMessage="Enter a Hyper-V virtual machine name",
ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
[ValidateNotNullorEmpty()]
[alias("vm")]
[object]$Name="*"
)

Begin {
Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
} #begin

Process {
if ($name -is [string]) {
Write-Verbose -Message "Getting virtual machine(s)"
Try {
$vms = Get-VM -Name $name -ErrorAction Stop
}
Catch {
Write-Warning "Failed to find a VM or VMs with a name like $name"
#bail out
Return
}
}
else {
#otherwise we'll assume $Name is a virtual machine object
Write-Verbose "Found one or more virtual machines matching the name"
$vms = $name
}

foreach ($vm in $vms) {
#get first drive file
$diskFile = Get-Item -Path $vm.HardDrives[0].Path
$vm.hardDrives[0] | Select-Object -property VMName,
@{Name="LastUse";Expression={$diskFile.LastWriteTime}},
@{Name="LastUseAge";Expression={(Get-Date) - $diskFile.LastWriteTime}}
}#foreach
} #process

End {
Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
} #end

I wrote this as a script file so that you could run it remotely using Invoke-Command against your Hyper-V server from your desktop. You could easily turn this into a function and/or modify it to include additional aging information if you find that useful. The script also has comment based help. But with this script there’s no limit to what I can do with the data. For example, I can send last use information via email

PS C:\> $body = C:\scripts\Get-VMLastUse.ps1 | sort LastUseAge | out-string
PS C:\> send-mailmessage -to [email protected] -from [email protected] -subject "Vast Use Report $(Get-Date)" -body $body

Or find virtual machines that haven’t been used in over 30 days:

PS C:\> C:\scripts\Get-VMLastUse.ps1 -erroraction SilentlyContinue | Where {$_.LastUseAge.TotalDays-gt 30} | Sort LastUse

Results are in Figure 4.

Figure 4

Or I could simply remove the virtual machine.

PS C:\> C:\scripts\Get-VMLastUse.ps1 -erroraction SilentlyContinue | Where {$_.LastUseAge.TotalDays -gt 30} | select -expand VMName | Remove-VM -whatif
What if: Remove-VM will remove virtual machine "XP Lab".
What if: Remove-VM will remove virtual machine "SystemCenter Demo".
What if: Remove-VM will remove virtual machine "Demo Rig".
What if: Remove-VM will remove virtual machine "CHI-Client02".

Is aging information useful to you? How do you use it? What other type of management information would be beneficial in managing your Hyper-V infrastructure? I’d truly like to know. In the mean time I hope you’ll try some of these PowerShell techniques. Enjoy.

 

 

Threat Monitor
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

126 thoughts on "Get VM Last Use Time"

  • Nagesh says:

    Thanks for your script , However my all VM VHD files are stored in storage .. Whevever i am running the script . It showing the current time not actual last time

  • og baller says:

    I have learn a few excellent stuff here. Definitely price bookmarking
    for revisiting. I wonder how so much effort you set to make this sort of magnificent
    informative web site.

  • Philip Waller says:

    I find it amazing that Microsoft in their infinite wisdom cannot expose this in VMM / Powershell? so if i have 50 Hyper V hosts i have to reach out to each host for each VM’s disk?

    Lunacy!

    • At the time, that was the best solution I could come up with. I’m going to dig into this again and see if there is a better way. Of course, you still need to enumerate each virtual machine on a host. But I’m hoping to find an easier way.

  • Logan Wang says:

    This is a great article. However, a key piece is missing, which I am wondering how to do it in customize property to define the last use, Thanks.

    Logan

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.