One of the Hyper-V-related questions I get asked most often is how to shutdown (or startup) Hyper-V virtual machines in a specific order. Unfortunately, neither the Hyper-V Manager nor System Center Virtual Machine Manager (VMM) offers a great option. In recent years, I have written a few different articles on the subject, but none of the techniques that I have outlined are what I would consider to be perfect. However, with a little bit of creativity, it is possible to control the order in which your VMs startup or shutdown. Doing so means combining several different techniques.
Hyper-V VMs startup and shutdown sequences using VMM
If your goal is to control Hyper-V virtual machines startup or shutdown sequences, then the first thing that I recommend doing is to create one or more scripts that can reside in a VMM library. That way, you can perform sequential starts and stops from within VMM. Even though the VMM libraries are probably best known for storing ISO files and virtual machine templates, you can just as easily store PowerShell scripts within the libraries. These scripts can be executed directly within VMM, as shown below.
Once you figure out where to store your script, the next step is to build the script. The way you will need to go about doing so will vary heavily depending on what you need the script to do. For example, if your goal is to build a script that causes VMs to start in a specific order (in the most literal way possible), then the script might look something like this:
Start-VM VM1 Start-VM VM2 Start-VM VM3
While this script does indeed start the virtual machines in a specific order, the script is lacking in every way imaginable. For instance, the script does not wait for one VM to boot before attempting to start the next VM.
If your goal is to make sure that a VM’s operating system has started before booting the next one, there are several ways that you can go about doing so. One option is to base the script around the Wait-VM cmdlet, which I have discussed in a previous article.
Another possible option is to create a script that actually checks to see if a VM is running before starting the next VM. Here is what such a script might look like:
Function Test-VM($VM){ $Status = Get-VM $VM If ($Status.State -eq 'Off') {Test-VM $VM} } ForEach($VM in Get-Content 'C:\Scripts\VMs.txt') { Write-Host $VM Start-VM $VM Test-VM $VM }
This script starts by reading a list of VMs from a text file. The text file would need to be a list of virtual machine names, listed in the order in which those VMs should be started.
The script contains a ForEach loop that allows the virtual machines in the list to be processed one at a time. For each VM, the script displays the virtual machine’s name on the screen, issues a command to start the VM, and then calls a function called Test-VM. This function’s job is to prevent any other VMs from being started until the virtual machine is confirmed to be running.
To do this, the function creates a variable named $Status and maps this variable to the virtual machine (using the Get-VM cmdlet). The next line of code checks the virtual machine state to see if the virtual machine is currently powered off. If the VM is currently off, then the function calls itself and runs the test again. It keeps doing this until the VM is found to be running. At that point, the function terminates, and the next VM is started. The script doesn’t really produce a lot of visible output, but you can see what it looks like in the figure below.
Obviously, this script is better than the first one that I showed you, but it still isn’t perfect. After all, this script only checks to see if the VM is running. It does nothing to make sure that the VM’s operating system has loaded and that the VM is ready for the next VM in the sequence to start. Fortunately, there is a way to accomplish this too, but because every VM is different, I won’t be able to give you a script that is universally applicable.
The trick is to pick out a system service within the VM whose status is representative of the VM as a whole. If, for example, you want to check to see if the Windows operating system is running, then you might check the status of the Windows Time Service or the Plug and Play Service. If, on the other hand, you are more interested in an application’s status, then you should pick a service tied to that application. If, for example, you want to make sure that SQL Server is running before booting the next VM, you might check to make sure that the SQL Server service is running (remember that this service must include the instance name).
To show you how such a script might work, take a look at the example below:
Function Check-Service($VM){ $Service = Get-Service DHCP -ComputerName $VM If ($Service.Status -eq 'Stopped') {Check-Service $VM} Write-Host 'DHCP service is running' } Function Test-VM($VM){ $Status = Get-VM $VM If ($Status.State -eq 'Off') {Test-VM $VM} Write-Host 'The server is running' Write-Host 'Checking DHCP service' Check-Service $VM } ForEach($VM in Get-Content 'C:\Scripts\VMs.txt') { Write-Host $VM Start-VM $VM Test-VM $VM }
This script is very similar to the script that I showed you a moment ago, but with one main difference. Once a VM has started, the script calls a function named Check-Service. This function continuously checks the status of the DHCP service to see whether or not it is running. Once the DHCP service is running, the function exits, and the next VM on the list is started.
Keep in mind that things are not always quite this simple in the real world since your script will need to run under a context that gives it the permissions necessary to interact with the virtual machine’s operating systems. The script will also need to be able to resolve the remote computer’s name.
Featured image: Shutterstock