Track Powered Off VMs in vCenter

Tracking powered off VMs is a struggle most people have. Depending on the number of VM Admins there are in your environment, each person can have a different way of tracking. We’ve tried renaming VMs, placing them in a “Powered Off” labeled folder, adding notes to the VM, but the real issue is that if it isn’t automated and you don’t include the date you aren’t ever sure what VMs you can safely delete.

As part of my Daily vCenter health script I now automatically add a date to each powered off VM so I know what VMs I can delete. The only prerequisite is to create a custom attribute called “OffDate” or anything else you want for the VMs.

First thing we do is get the current date in a readable and reportable format. So we use the $date variable and get today’s date then convert it to month-day-year format:

$date = (Get-Date).ToString('MM-dd-yyyy')

After that we get a list of VMs that are currently powered off and that don’t live in a folder that contains the word “template”. In our environment we have a handful of VMs that haven’t been converted to templates, but are used as templates for provisioning. I don’t want to accidentally tag and delete these so they get excluded.

$vmlist = Get-VM | Where {$_.PowerState -eq "PoweredOff" -AND $_.Folder -notlike "*template*"}

Then we create a variable of the custom attribute we’ll be searching for.

$attributeName = "OffDate"

Now we need to gather a list of VMs that are Powered off, but do not currently have their custom attribute populated. We are doing a Get-Annotation for that attribute name we just defined and looking for a value that is empty.

$noDateVM = ForEach ($vm in $vmlist) {$vm | Get-Annotation -CustomAttribute $attributeName | Where {$_.value -eq ""}}

Once we have a list of each of those VMs, we can now tag each of these VMs with the current date. I capture this as a variable so I can add the output to my daily report. I like to know what VMs are getting tagged in case something is added by mistake or an important VM was powered off that shouldn’t have been.

$offDateList = ForEach ($blankVM in $noDateVM) {$blankVM.AnnotatedEntity | Set-Annotation -CustomAttribute $attributename -Value $date}


With the tagging complete, we can now add it to our report. I do HTML formatted reports and I don’t want the section to be in the email if it’s empty. In this portion we check to see if $offDateList is empty and if it is we don’t do anything else. However, if there are VMs, we then create the output with a title and a list the VM along with today’s date.

IF (!$offDateList) {} ELSE {
$outputdateOutput = $offDateList | Select @{N="VM Name";E={$_.AnnotatedEntity}},Value | ConvertTo-Html -PreContent "<h4>New Powered Off VMs</h4>" | Out-String

Here is the complete script:

$date = (Get-Date).ToString('MM-dd-yyyy')
$vmlist = Get-VM | Where {$_.PowerState -eq "PoweredOff" -AND $_.Folder -notlike "*template*"}
$attributeName = "OffDate"
$noDateVM = ForEach ($vm in $vmlist) {$vm | Get-Annotation -CustomAttribute $attributeName | Where {$_.value -eq ""}}
$offDateList = ForEach ($blankVM in $noDateVM) {$blankVM.AnnotatedEntity | Set-Annotation -CustomAttribute $attributename -Value $date}
IF (!$offDateList) {} ELSE {
$outputdateOutput = $offDateList | Select @{N="VM Name";E={$_.AnnotatedEntity}},Value | ConvertTo-Html -PreContent "<h4>New Powered Off VMs</h4>" | Out-String }

But wait, there’s more.

Sure, we can tag VMs, but now we need to report on any VMs that have a Powered Off date, but are currently running.
First thing we do is grab a list of Powered On VMs:

$poweredOnVMs = Get-VM | Where {$_.PowerState -eq "PoweredOn"}

Now for each of these VMs we want to find those that have an OffDate that isn’t empty:

$runningVMs = ForEach ($VM in $poweredOnVMs) {
$vm | Get-Annotation -CustomAttribute $attributeName | Where {$_.value -ne ""}}

Once again, if the list is empty, I don’t want it to show up in my report so if it’s empty we don’t do anything else. If not, we create our output:

IF (!$runningVMs) {} ELSE {
$outputrunningVMReport = $runningVMs | Select @{N="VM name";E={$_.AnnotatedEntity}},Value | ConvertTo-Html -PreContent "<h4>Running VMs with Powered Off Date</h4>" | Out-String }

And here is the full script:

$poweredOnVMs = Get-VM | Where {$_.PowerState -eq "PoweredOn"}
$runningVMs = ForEach ($VM in $poweredOnVMs) {
$vm | Get-Annotation -CustomAttribute $attributeName | Where {$_.value -ne ""}}
IF (!$runningVMs) {} ELSE {
$outputrunningVMReport = $runningVMs | Select @{N="VM name";E={$_.AnnotatedEntity}},Value | ConvertTo-Html -PreContent "<h4>Running VMs with Powered Off Date</h4>" | Out-String }

Leave a Reply

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