Save to My DOJO
Table of contents
If you’ve been reading my posts, you should realize I am a huge proponent of Windows PowerShell. The number one attraction for me, is that if I can run a command to process a single thing, like a file, user or Hyper-V virtual machine, I can probably do the same to 10, 100 or 1000 with very little additional effort.
For example, let me demonstrate how easy it is to control the state of Hyper-V virtual machines using PowerShell and the Hyper-V 3.0 module. There may be situations, rare I hope, where you might need to suspend, resume or even shutdown all the running virtual machines on a given host. Instead of resorting to the Hyper-V Manager, open a PowerShell prompt instead.
Let’s say you need to suspend or pause all running virtual machines. This is a simple one line PowerShell command.
get-vm | where state -eq 'running' | suspend-vm
For the sake of simplicity I’m running these commands in an interactive PowerShell session. But you could also run them from a remote computer using PowerShell remoting. The remote computer doesn’t need any Hyper-V bits. In fact it could even be running PowerShell 2.0! Take the command and wrap it in an Invoke-Command expression.
Invoke-Command { get-vm | where state -eq 'running' | suspend-vm } –computername HV01
I wish Get-VM had a parameter to filter in place based on the state, but it doesn’t so I have to pipe to Where-Object. Cmdlets like Resume-VM will take virtual machine names as a parameter, but I will get an error if the VM is not in the proper state. So I get all the virtual machines I’m interested in by name (in this case all of them) and then filter out those that are not in the proper state. This will come in handy later. Also, note that I’m using the new PowerShell v3 syntax, again for the sake of simplicity. When the time comes to get back to work, resume all paused virtual machines.
get-vm | where state -eq 'paused' | resume-vm
If you want to save the state of each machine, essentially hibernating it, all you need to do is change the state and appropriate cmdlet. So to save them:
get-vm | where state -eq 'running' | stop-vm –Save
And to re-start them:
get-vm | where state -eq 'saved' | start-vm
These operations process all virtual machines pretty much at once. But let’s say you need an emergency shutdown process, or you are automating a maintenance process that requires all virtual machines to be shutdown. You could run a command like this:
get-vm | where state -eq 'running' | stop-vm
[optin-monster-shortcode id=”lwzgfpb294pntqna”]
You could even run it as a background job.
get-vm | where state -eq 'running' | stop-vm -asjob
With this technique there is no telling which virtual machines will shut down first. But perhaps you need a more orderly shutdown. Perhaps you want to stop all domain member or stand-alone servers first and then domain controllers in an orderly fashion. A command like this will get the job done.
$name = "CHI-FP02","CHI-FP01","CHI-DEV01","CHI-Client02","CHI-APP01","CHI-DC04","CHI-DC02","CHI-DC01" get-vm $name | where state -eq 'running' | Stop-VM -Save
Each VM will be saved in the order listed. If you want to save some typing, put the names in a text file in the order you want processed.
get-vm (get-content c:workVMs.txt) | where state -eq 'running' | Stop-VM
If you don’t have too many virtual machines this runs pretty quickly. However, since the domain members can theoretically be stopped in any order or even at the same time, an alternative is to use a PowerShell workflow and take advantage of its parallelism feature. In a workflow I can shutdown all member servers at the same time. Then I can process my domain controllers in an orderly fashion. Here’s the workflow I came up with.
#requires -version 3.0 <# This workflow will take a collection of Hyper-V virtual machine names in 2 categories. Low Priority VMs can be shut down in parallel. These would be standalone or domain members with no dependencies other than maybe a domain controller. High priority machines must be shut down in a given order. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This code is offered as-as with no warranty or guarantee. !! !! Please test in a non-production environment. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #> Workflow Stop-MyVms { Param([string[]]$LowPriority,[string[]]$HighPriority) #stop low-priority machines in parallel Sequence { Write-Verbose -Message "Processing low priority virtual machines in parallel" foreach -parallel ($name in $LowPriority) { Write-Verbose -Message "Analyzing $name" Try { $vm = Get-VM -name $using:name -ErrorAction Stop if ($vm.state -eq 'running') { Write-Verbose -Message "..Stopping $($vm.name)" Stop-VM -Name $vm.name } #if state = running } #Try Catch { Write-Warning -Message "Failed to get virtual machine $name" } #Catch } #foreach parallel } #sequence #stop high priority machines in sequence Sequence { Write-Verbose -Message "Processing high priority virtual machines in order" foreach ($name in $HighPriority) { Write-Verbose -Message "Analyzing $name" Try { $vm = Get-VM -name $using:name -ErrorAction Stop if ($vm.state -eq 'running') { Write-Verbose -Message "..Stopping $($vm.name)" Stop-VM -Name $vm.name } #if state = running } #Try Catch { Write-Warning -Message "Failed to get virtual machine $name" } #catch } #foreach } #sequence } #close workflow
The workflow has two defined parameters. Pass a list of virtual machine names to –LowPriority that can be shut down quickly and in parallel. The list of names for –HighPriority will be shut down in the order specified. You could run this interactively on the server, but I like being able to run it remotely. Because my Hyper-V box and client are both running PowerShell 3.0, I can run it remotely, feeding in a list of computer names. With workflow you automatically get parameters for –PSComputername and –Asjob so from my desktop I can run a command like this once the workload has been loaded into my PowerShell session:
Stop-MyVMs –lowpriority (get-content c:workmembers.txt) –highpriority (get-content c:workdc.txt) –pscomputername HV01 –asjob
The text files are on the desktop computer but the names will be processed by the server. Or you could define variables ahead of time for the computer names. If by chance you use an incorrect name, you’ll see a warning message.
Controlling the state of all Hyper-V virtual machines is really quite simple once you have some basic PowerShell knowledge. And even though I know the GUI isn’t complicated, using the command line leads to many other opportunities such as delegation, scheduled automatic maintenance and overall efficiency.
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!
3 thoughts on "Manage Hyper-V with PowerShell"
Nice! And you managed to ‘light’ the power-shell lamp here as well. C#-Shell
What about modifying this to handle VMs within a cluster?