I use Hyper-V frequently for testing purposes. Virtualization has a lot of advantages. There is one thing (ok, there is more but let’s concentrate on a particular one today) that bothers me. All my machines joined our company Active Directory. The problem is that if a machine doesn’t run for a certain time (I guess something like 2 weeks) the machine account will be removed from Active Directory. It’s not a huge deal, you just have to:
- login as administrator
- remove your computer from the domain
- restart
- join the domain again
- restart
Even thought it’s not such a big deal it annoys me every time I need to do that. I thought that if I would be able to automatically start each machine (let’s say at night when I’m not using the computer) it would solve this problem. Since I use PowerShell I wanted to be able to leverage PowerShell for this.
Retrieve all available VMs
To retrieve all available VMs we need to execute a WMI query. The trick with this query is that it will retrieve all machines including your host machine. That’s why you need to add additional filter where you ask to retrieve only machines which caption starts with “Virtual”.
function get-vms
{
$query = "SELECT * FROM MsVM_ComputerSystem WHERE Caption Like 'Virtual%' "
get-wmiobject -query $query -namespace "root\virtualization" -computername "."
}
Retrieve a particular VM
Once we have a collection of all available VMs, we will execute some actions on them. As a helper I will also create a function that will retrieve VM object when provided a machine name:
function get-vm([string] $name)
{
#Get the VM Object
$query = "SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" + $name + "'"
get-wmiobject -query $query -namespace "root\virtualization" -computername "."
}
Starting VM
To start a VM, we just need to set RequestStateChange to value 2. The code to do so is very simple.
function start-vm([string] $name)
{
$VM = get-vm $name
#Request a state change on the VM
$result = $VM.RequestStateChange(2)
}
Shutting down VM
To shutdown a VM, we need to get a special ShutdownComponent and call InitiateShutdown method. The code might seem complex but it isn’t. Most of the code bellow is just error handling code.
function shutdown-vm([string] $name)
{
#Get the Shutdown Component for the VM
$vm = get-vm $name
$query = "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" + $vm.name + "'"
$Shutdown = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
if ($Shutdown -ne $null)
{
#Request a forced shutdown
$result = $Shutdown.InitiateShutdown($true,"Shutdown initiated from PowerShell script")
if ($result.returnValue -eq 0)
{
write-host "Shutdown of '$name' started."
}
else
{
write-host "Attempt to shutdown 'name' failed with code $($result.returnValue)."
}
}
else
{
write-host "Could not get shutdown component for '$name'."
}
}
Final script
To put it all together we need to retrieve all VMs and call start and shutdown in a loop for each of the retrieved VMs.
$VMs = get-vms
foreach ($VM in $VMs)
{
$name = $VM.ElementName
write-host $name
write-host "`tStarting...."
start-vm $name
write-host "`tWaiting to fully load for 7 minutes..."
start-sleep -seconds 420
write-host "`tShutdown..."
shutdown-vm $name
}
write-host "Finished."
Let me note that in the previous script I added a sleep for 7 minutes. Why do I do that? I want to give the VM enough time to completely start all the services.
Remarks
Let me add that I found that shutdown can also be done using “associators”. In the above code we use the following query:
$vm = get-vm $name
$query = "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" + $vm.name + "'"
$Shutdown = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
The same can be done using “associators”.
$vm = get-vm $name
$query = "Associators of {$vm} Where AssocClass=Msvm_SystemDevice ResultClass=Msvm_ShutdownComponent"
$Shutdown = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
I don’t know which one is better but both of them seems to work just fine on my machine. If you do know the difference then please let me know.
More information
More information can be found at:
Attachments:
PowerShell
powershell