Hi guys,
I need some help in putting a script together that will remove all powered off virtual machines that have been powered off longer than 14 days and will send out an e-mail with the virtual machines that were removed. Using the code below I am able to retrieve the desired virtual machines that match the date but can't seem to be able to use the same variables to pipe them into the Remove-VM commandlet.
Code:
$vms = Get-VM | where {$_.PowerState -eq "PoweredOff"}
$vmPoweredOff = $vms | %{$_.Name}
$events = Get-VIEvent -Start (Get-Date).AddDays(-14) -Entity $vms | where{$_.FullFormattedMessage -like "*is powered off"}
Send-MailMessage -From virtualadmin@company.com -To vadmin@company.com -SmtpServer smtprelay.company.com
-Subject "Removed $($14day) VM" -Body ($vmNames | Out-String) ----> The body would only include the virtual machines that were removed.
Much appreciated. Thanks!!
I'm a bit confused.
Originally you said "...remove all powered off virtual machines that have been powered off longer than 14 days"
That's what my script does.
Now you seem to ask that the VMs that have been powered off during the last 14 days should be removed.
Which is it?
In any case, to remove the VMs that have been powered off longer than 14 days ago, you can do
$vms = @{}
Get-VM | where {$_.PowerState -eq "PoweredOff"} | % {$vms.Add($_.Name, $_)}
Get-VIEvent -Start (Get-Date).AddDays(-14) -Entity $vms.Values -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} |
Sort-Object -Property CreatedTime -Unique | % {
$vms.Remove($_.VM.Name)
}
Remove-VM -VM $vms.Values -DeletePermanently -Confirm:$false -WhatIf
$sMail = @{
From = 'virtualadmin@company.com'
To = 'vadmin@company.com'
SmtpServer = 'smtprelay.company.com'
Subject = "Removed $($vms.Keys.Count) VM powered off more than 14 days"
Body = ($vms.Keys | Out-String)
}
Send-MailMessage @sMail
And to remove that VMs that have been powered off during the last 14 days, you can do
$vms = @{}
$vm = Get-VM | where{$_.PowerState -eq 'PoweredOff'}
Get-VIEvent -Start (Get-Date).AddDays(-14) -Entity $vm -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} |
Sort-Object -Property CreatedTime -Unique | % {
$vms.Add($_.VM.Name,(Get-VM -Name $_.VM.Name))
}
Remove-VM -VM $vms.Values -DeletePermanently -Confirm:$false -WhatIf
$sMail = @{
From = 'virtualadmin@company.com'
To = 'vadmin@company.com'
SmtpServer = 'smtprelay.company.com'
Subject = "Removed $($vms.Keys.Count) VM powered during the last 14 days"
Body = ($vms.Keys | Out-String)
}
Send-MailMessage @sMail
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Try something like this.
Since you stated that you wanted to remove VMs powered off more than 14 days ago, I used a hash table with all powered off VMs.
Then I remove the VMs that have been powered off during the last 14 days from that hash table.
Note that I used the Unique switch on the Sort-Object, since a VM can be powered off more than once during the last 14 days.
And I used splatting for the Send-MailMessage parameters to improve readability.
Get-VM | where {$_.PowerState -eq "PoweredOff"} | % {$vms.Add($_.Name, $_)}
Get-VIEvent -Start (Get-Date).AddDays(-14) -Entity $vms.Values -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} |
Sort-Object -Property CreatedTime -Unique | % {
$vms.Remove($_.VM.Name)
}
$sMail = @{
From = 'virtualadmin@company.com'
To = 'vadmin@company.com'
SmtpServer = 'smtprelay.company.com'
Subject = "Removed $($vms.Keys.Count) VM powered off more than 14 days"
Body = ($vms.Keys | Out-String)
}
Send-MailMessage @sMail
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi Luc,
Thank you for helping me on this one, I do appreciate it. I modified your code a little so that I can use it to test recently powered off virtual machines. So far it is able to retrieve the desired virtual machines based on the Get-VIEvent being specified and is also able to send out the e-mail but it did not remove the any of the virtual machines that made the list.
Code:
$vms = @{}
Get-VM | where {$_.PowerState -eq "PoweredOff"} | % {$vms.Add($_.Name, $_)}
Get-VIEvent -Start (Get-Date).AddHours(-1) -Entity $vms.Values -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} | Sort-Object -Property CreatedTime -Unique | % {$vms.Remove($_.VM.Name)}
I used the AddHours so that I can test with virtual machines that were recently powered off since I don't want to remove the older ones yet until I know the script will work as intended. The above modification does produce the list but does not remove them. Btw - Is this line of code "$vms.Remove($_.VM.Name" actually doing the same thing as "Remove-VM"?
That is correct, there is no Remove-VM in the script I posted.
I based myself on the lines of code you posted.
And no, the expression $vms.Remove($_.VM.Name) removes the VM from the hash table that is used.
That is not removing the VM itself.
You can remove all the selected VMs by placing this line before the Send-MailMessage lines
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks Luc! Great tip on the "-WhatIf" statement cause if looks like the code would have removed 13 powered off virtual machines. Here's what I have found in executing the code with different values.
If I execute the code (below) without the "| % {$vms.Remove($_.VM.Name)}" line, I will see the virtual machines that I recently powered off and will also see that the code would have removed all of the virtual machines that are in a powered off state. See output below.
Code:
Get-VIEvent -Start (Get-Date -Hour 12 -Minute 00) -Finish (Get-Date -Hour 15 -Minute 40) -Entity $vms.Values -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} | Sort-Object -Property CreatedTime -Unique
Template : False
Key : 7736027
ChainId : 7736024
CreatedTime : 10/15/2018 2:16:03 PM
UserName : *******
Datacenter : VMware.Vim.DatacenterEventArgument
ComputeResource : VMware.Vim.ComputeResourceEventArgument
Host : VMware.Vim.HostEventArgument
Vm : VMware.Vim.VmEventArgument
Ds :
Net :
Dvs :
FullFormattedMessage : TEST-T02 on esx.somecompany in Texas is powered off
ChangeTag :
Template : False
Key : 7736099
ChainId : 7736097
CreatedTime : 10/15/2018 2:32:57 PM
UserName : ******
Datacenter : VMware.Vim.DatacenterEventArgument
ComputeResource : VMware.Vim.ComputeResourceEventArgument
Host : VMware.Vim.HostEventArgument
Vm : VMware.Vim.VmEventArgument
Ds :
Net :
Dvs :
FullFormattedMessage : TEST-T03 on esx.somecompany in Texas is powered off
ChangeTag :
Template : False
Key : 7736775
ChainId : 7736771
CreatedTime : 10/15/2018 3:38:47 PM
UserName : ******
Datacenter : VMware.Vim.DatacenterEventArgument
ComputeResource : VMware.Vim.ComputeResourceEventArgument
Host : VMware.Vim.HostEventArgument
Vm : VMware.Vim.VmEventArgument
Ds :
Net :
Dvs :
FullFormattedMessage : TEST-T03 on esx.somecompany in Texas is powered off
ChangeTag :
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-01'
What if: Performing operation 'Removing VM from disk.' on VM 'TEST-T03'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-02'
What if: Performing operation 'Removing VM from disk.' on VM 'TEST-T01'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-03'
What if: Performing operation 'Removing VM from disk.' on VM 'TEST-T02'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-04'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-05'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-06'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-07'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-08'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-09'
If I execute the same code (below) and include the "| % {$vms.Remove($_.VM.Name)}" line, then I will NOT see the virtual machines that I recently powered off and will only see the ones that have been powered for almost a month. The e-mail also does NOT include the recently powered off virtual machines and only includes the ones below. See output below.
Code:
Get-VIEvent -Start (Get-Date -Hour 12 -Minute 00) -Finish (Get-Date -Hour 15 -Minute 40) -Entity $vms.Values -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} | Sort-Object -Property CreatedTime -Unique | % {$vms.Remove($_.VM.Name)}
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-01'
What if: Performing operation 'Removing VM from disk.' on VM 'TEST-T03'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-02'
What if: Performing operation 'Removing VM from disk.' on VM 'TEST-T01'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-03'
What if: Performing operation 'Removing VM from disk.' on VM 'TEST-T02'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-04'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-05'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-06'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-07'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-08'
What if: Performing operation 'Removing VM from disk.' on VM 'ABC-09'
Is it possible for the logic of the script to only include the VMs what will be removed or that have been removed as long as they meet the event criteria?
Thanks again for your help sir.
I'm a bit confused.
Originally you said "...remove all powered off virtual machines that have been powered off longer than 14 days"
That's what my script does.
Now you seem to ask that the VMs that have been powered off during the last 14 days should be removed.
Which is it?
In any case, to remove the VMs that have been powered off longer than 14 days ago, you can do
$vms = @{}
Get-VM | where {$_.PowerState -eq "PoweredOff"} | % {$vms.Add($_.Name, $_)}
Get-VIEvent -Start (Get-Date).AddDays(-14) -Entity $vms.Values -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} |
Sort-Object -Property CreatedTime -Unique | % {
$vms.Remove($_.VM.Name)
}
Remove-VM -VM $vms.Values -DeletePermanently -Confirm:$false -WhatIf
$sMail = @{
From = 'virtualadmin@company.com'
To = 'vadmin@company.com'
SmtpServer = 'smtprelay.company.com'
Subject = "Removed $($vms.Keys.Count) VM powered off more than 14 days"
Body = ($vms.Keys | Out-String)
}
Send-MailMessage @sMail
And to remove that VMs that have been powered off during the last 14 days, you can do
$vms = @{}
$vm = Get-VM | where{$_.PowerState -eq 'PoweredOff'}
Get-VIEvent -Start (Get-Date).AddDays(-14) -Entity $vm -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} |
Sort-Object -Property CreatedTime -Unique | % {
$vms.Add($_.VM.Name,(Get-VM -Name $_.VM.Name))
}
Remove-VM -VM $vms.Values -DeletePermanently -Confirm:$false -WhatIf
$sMail = @{
From = 'virtualadmin@company.com'
To = 'vadmin@company.com'
SmtpServer = 'smtprelay.company.com'
Subject = "Removed $($vms.Keys.Count) VM powered during the last 14 days"
Body = ($vms.Keys | Out-String)
}
Send-MailMessage @sMail
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
My mistake for not being specific and clear sir. I will test these and will let you know how it goes. Thank you very much!
Hi Luc,
All is working with the scripts you provided as you would expect. Thank you for this!! BTW - Is there a way I can include a line in the script to exclude a VM from being removed even though they have been powered off longer than 14 days? This is in case a user wants to keep the VM around for what ever reason.
Here's a sampIe of another script I use to clean up snapshots older than 3 days but exclude any snaps that need to remain longer.
Code:
Get-VM | where-object {$_.Name -notmatch "VM"} | Get-Snapshot | Where {$_.Created -lt (Get-Date).AddDays(-3)} | Remove-Snapshot -confirm:$false
Thanks again sir!